1 /* NetHack 3.6 wintty.c $NHDT-Date: 1520825319 2018/03/12 03:28:39 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.142 $ */
2 /* Copyright (c) David Cohrs, 1991 */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* JNetHack Copyright */
6 /* (c) Issei Numata 1994-2000 */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2019 */
8 /* JNetHack may be freely redistributed. See license for details. */
11 * Neither a standard out nor character-based control codes should be
12 * part of the "tty look" windowing implementation.
16 /* It's still not clear I've caught all the cases for H2344. #undef this
17 * to back out the changes. */
26 #define MICRO /* The Mac is a MICRO only for this file, not in general! */
28 extern void msmsg(const char *, ...);
38 #ifdef CLIPPING /* might want SIGWINCH */
39 #if defined(BSD) || defined(ULTRIX) || defined(AIX_31) || defined(_BULL_SOURCE)
44 #ifdef TTY_TILES_ESCCODES
45 extern short glyph2tile[];
46 #define TILE_ANSI_COMMAND 'z'
47 #define AVTC_GLYPH_START 0
48 #define AVTC_GLYPH_END 1
49 #define AVTC_SELECT_WINDOW 2
50 #define AVTC_INLINE_SYNC 3
53 extern char mapped_menu_cmds[]; /* from options.c */
55 /* this is only needed until tty_status_* routines are written */
56 extern NEARDATA winid WIN_STATUS;
58 /* Interface definition, for windows.c */
59 struct window_procs tty_procs = {
63 | WC_TILED_MAP | WC_ASCII_MAP
68 | WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN),
70 #if defined(SELECTSAVED)
73 #if defined(STATUS_HILITES)
74 | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS
77 tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event,
78 tty_exit_nhwindows, tty_suspend_nhwindows, tty_resume_nhwindows,
79 tty_create_nhwindow, tty_clear_nhwindow, tty_display_nhwindow,
80 tty_destroy_nhwindow, tty_curs, tty_putstr, genl_putmixed,
81 tty_display_file, tty_start_menu, tty_add_menu, tty_end_menu,
82 tty_select_menu, tty_message_menu, tty_update_inventory, tty_mark_synch,
88 tty_update_positionbar,
90 tty_print_glyph, tty_raw_print, tty_raw_print_bold, tty_nhgetch,
91 tty_nh_poskey, tty_nhbell, tty_doprev_message, tty_yn_function,
92 tty_getlin, tty_get_ext_cmd, tty_number_pad, tty_delay_output,
93 #ifdef CHANGE_COLOR /* the Mac uses a palette device */
96 tty_change_background, set_tty_font_name,
101 /* other defs that really should go away (they're tty specific) */
102 tty_start_screen, tty_end_screen, genl_outrip,
104 nttty_preference_update,
106 genl_preference_update,
108 tty_getmsghistory, tty_putmsghistory,
110 genl_status_finish, genl_status_enablefield,
111 #ifdef STATUS_HILITES
116 genl_can_suspend_yes,
119 static int maxwin = 0; /* number of windows in use */
121 struct WinDesc *wins[MAXWIN];
122 struct DisplayDesc *ttyDisplay; /* the tty display descriptor */
124 extern void FDECL(cmov, (int, int)); /* from termcap.c */
125 extern void FDECL(nocmov, (int, int)); /* from termcap.c */
126 #if defined(UNIX) || defined(VMS)
127 static char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */
130 static char winpanicstr[] = "Bad window id %d";
131 char defmorestr[] = "--More--";
134 #if defined(USE_TILES) && defined(MSDOS)
135 boolean clipping = FALSE; /* clipping on? */
136 int clipx = 0, clipxmax = 0;
138 static boolean clipping = FALSE; /* clipping on? */
139 static int clipx = 0, clipxmax = 0;
141 static int clipy = 0, clipymax = 0;
142 #endif /* CLIPPING */
144 #if defined(USE_TILES) && defined(MSDOS)
145 extern void FDECL(adjust_cursor_flags, (struct WinDesc *));
148 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
149 boolean GFlag = FALSE;
150 boolean HE_resets_AS; /* see termcap.c */
153 #if defined(MICRO) || defined(WIN32CON)
154 static const char to_continue[] = "to continue";
155 #define getret() getreturn(to_continue)
157 STATIC_DCL void NDECL(getret);
159 STATIC_DCL void FDECL(erase_menu_or_text,
160 (winid, struct WinDesc *, BOOLEAN_P));
161 STATIC_DCL void FDECL(free_window_info, (struct WinDesc *, BOOLEAN_P));
162 STATIC_DCL void FDECL(dmore, (struct WinDesc *, const char *));
163 STATIC_DCL void FDECL(set_item_state, (winid, int, tty_menu_item *));
164 STATIC_DCL void FDECL(set_all_on_page, (winid, tty_menu_item *,
166 STATIC_DCL void FDECL(unset_all_on_page, (winid, tty_menu_item *,
168 STATIC_DCL void FDECL(invert_all_on_page, (winid, tty_menu_item *,
169 tty_menu_item *, CHAR_P));
170 STATIC_DCL void FDECL(invert_all, (winid, tty_menu_item *,
171 tty_menu_item *, CHAR_P));
172 STATIC_DCL void FDECL(toggle_menu_attr, (BOOLEAN_P, int, int));
173 STATIC_DCL void FDECL(process_menu_window, (winid, struct WinDesc *));
174 STATIC_DCL void FDECL(process_text_window, (winid, struct WinDesc *));
175 STATIC_DCL tty_menu_item *FDECL(reverse, (tty_menu_item *));
176 STATIC_DCL const char *FDECL(compress_str, (const char *));
177 STATIC_DCL void FDECL(tty_putsym, (winid, int, int, CHAR_P));
178 STATIC_DCL void FDECL(bail, (const char *)); /* __attribute__((noreturn)) */
179 STATIC_DCL void FDECL(setup_rolemenu, (winid, BOOLEAN_P, int, int, int));
180 STATIC_DCL void FDECL(setup_racemenu, (winid, BOOLEAN_P, int, int, int));
181 STATIC_DCL void FDECL(setup_gendmenu, (winid, BOOLEAN_P, int, int, int));
182 STATIC_DCL void FDECL(setup_algnmenu, (winid, BOOLEAN_P, int, int, int));
183 STATIC_DCL boolean NDECL(reset_role_filtering);
186 * A string containing all the default commands -- to add to a list
187 * of acceptable inputs.
189 static const char default_menu_cmds[] = {
190 MENU_FIRST_PAGE, MENU_LAST_PAGE, MENU_NEXT_PAGE,
191 MENU_PREVIOUS_PAGE, MENU_SELECT_ALL, MENU_UNSELECT_ALL,
192 MENU_INVERT_ALL, MENU_SELECT_PAGE, MENU_UNSELECT_PAGE,
193 MENU_INVERT_PAGE, MENU_SEARCH, 0 /* null terminator */
196 #ifdef TTY_TILES_ESCCODES
197 static int vt_tile_current_window = -2;
200 print_vt_code(i, c, d)
203 if (iflags.vt_tiledata) {
205 if (i == AVTC_SELECT_WINDOW) {
206 if (c == vt_tile_current_window) return;
207 vt_tile_current_window = c;
210 printf("\033[1;%d;%d;%d%c", i, c, d, TILE_ANSI_COMMAND);
212 printf("\033[1;%d;%d%c", i, c, TILE_ANSI_COMMAND);
214 printf("\033[1;%d%c", i, TILE_ANSI_COMMAND);
219 # define print_vt_code(i, c, d) ;
220 #endif /* !TTY_TILES_ESCCODES */
221 #define print_vt_code1(i) print_vt_code((i), -1, -1)
222 #define print_vt_code2(i,c) print_vt_code((i), (c), -1)
223 #define print_vt_code3(i,c,d) print_vt_code((i), (c), (d))
226 /* clean up and quit */
232 tty_exit_nhwindows(mesg);
233 nh_terminate(EXIT_SUCCESS);
237 #if defined(SIGWINCH) && defined(CLIPPING)
238 STATIC_DCL void FDECL(winch_handler, (int));
241 * This really ought to just set a flag like the hangup handler does,
242 * then check the flag at "safe" times, in case the signal arrives
243 * while something fragile is executing. Better to have a brief period
244 * where display updates don't fit the new size than for tty internals
245 * to become corrupted.
247 * 'winch_seen' has been "notyet" for a long time....
251 winch_handler(sig_unused) /* signal handler is called with at least 1 arg */
252 int sig_unused UNUSED;
254 int oldLI = LI, oldCO = CO, i;
255 register struct WinDesc *cw;
259 #define WINCH_MESSAGE "(SIGWINCH)"
261 (void) write(fileno(wc_tracelogf), WINCH_MESSAGE,
262 strlen(WINCH_MESSAGE));
267 /* For long running events such as multi-page menus and
268 * display_file(), we note the signal's occurance and
269 * hope the code there decides to handle the situation
270 * and reset the flag. There will be race conditions
271 * when handling this - code handlers so it doesn't matter.
276 if ((oldLI != LI || oldCO != CO) && ttyDisplay) {
277 ttyDisplay->rows = LI;
278 ttyDisplay->cols = CO;
280 cw = wins[BASE_WINDOW];
281 cw->rows = ttyDisplay->rows;
282 cw->cols = ttyDisplay->cols;
284 if (iflags.window_inited) {
285 cw = wins[WIN_MESSAGE];
286 cw->curx = cw->cury = 0;
288 tty_destroy_nhwindow(WIN_STATUS);
289 WIN_STATUS = tty_create_nhwindow(NHW_STATUS);
293 if (CO < COLNO || LI < ROWNO + 3) {
295 tty_cliparound(u.ux, u.uy);
301 i = ttyDisplay->toplin;
302 ttyDisplay->toplin = 0;
305 ttyDisplay->toplin = i;
310 for (i = WIN_INVEN; i < MAXWIN; i++)
311 if (wins[i] && wins[i]->active) {
313 addtopl("Press Return to continue: ");
316 (void) fflush(stdout);
327 tty_init_nhwindows(argcp, argv)
334 * Remember tty modes, to be restored on exit.
336 * gettty() must be called before tty_startup()
337 * due to ordering of LI/CO settings
338 * tty_startup() must be called before initoptions()
339 * due to ordering of graphics settings
341 #if defined(UNIX) || defined(VMS)
342 setbuf(stdout, obuf);
346 /* to port dependant tty setup */
347 tty_startup(&wid, &hgt);
348 setftty(); /* calls start_screen */
350 /* set up tty descriptor */
351 ttyDisplay = (struct DisplayDesc *) alloc(sizeof(struct DisplayDesc));
352 ttyDisplay->toplin = 0;
353 ttyDisplay->rows = hgt;
354 ttyDisplay->cols = wid;
355 ttyDisplay->curx = ttyDisplay->cury = 0;
356 ttyDisplay->inmore = ttyDisplay->inread = ttyDisplay->intr = 0;
357 ttyDisplay->dismiss_more = 0;
359 ttyDisplay->color = NO_COLOR;
361 ttyDisplay->attrs = 0;
363 /* set up the default windows */
364 BASE_WINDOW = tty_create_nhwindow(NHW_BASE);
365 wins[BASE_WINDOW]->active = 1;
367 ttyDisplay->lastwin = WIN_ERR;
369 #if defined(SIGWINCH) && defined(CLIPPING) && !defined(NO_SIGNAL)
370 (void) signal(SIGWINCH, (SIG_RET_TYPE) winch_handler);
373 tty_clear_nhwindow(BASE_WINDOW);
375 tty_putstr(BASE_WINDOW, 0, "");
376 for (i = 1; i <= 4; ++i)
377 tty_putstr(BASE_WINDOW, 0, copyright_banner_line(i));
378 tty_putstr(BASE_WINDOW, 0, "");
379 #if 0 /*JP*//*JPTB reversed*/
380 tty_putstr(BASE_WINDOW, 0, JA_COPYRIGHT_BANNER_A);
381 tty_putstr(BASE_WINDOW, 0, JA_COPYRIGHT_BANNER_B);
382 tty_putstr(BASE_WINDOW, 0, JA_COPYRIGHT_BANNER_C);
383 tty_putstr(BASE_WINDOW, 0, JA_COPYRIGHT_BANNER_D);
384 tty_putstr(BASE_WINDOW, 0, "");
386 tty_display_nhwindow(BASE_WINDOW, FALSE);
389 /* try to reduce clutter in the code below... */
390 #define ROLE flags.initrole
391 #define RACE flags.initrace
392 #define GEND flags.initgend
393 #define ALGN flags.initalign
396 tty_player_selection()
398 int i, k, n, choice, nextpick;
399 boolean getconfirmation, picksomething;
401 char pbuf[QBUFSZ], plbuf[QBUFSZ];
404 menu_item *selected = 0;
406 /* Used to avoid "Is this ok?" if player has already specified all
407 * four facets of role.
408 * Note that rigid_role_checks might force any unspecified facets to
409 * have a specific value, but that will still require confirmation;
410 * player can specify the forced ones if avoiding that is demanded.
412 picksomething = (ROLE == ROLE_NONE || RACE == ROLE_NONE
413 || GEND == ROLE_NONE || ALGN == ROLE_NONE);
415 * choose randomly without asking for all unspecified facets.
417 if (flags.randomall && picksomething) {
418 if (ROLE == ROLE_NONE)
420 if (RACE == ROLE_NONE)
422 if (GEND == ROLE_NONE)
424 if (ALGN == ROLE_NONE)
428 /* prevent unnecessary prompting if role forces race (samurai) or gender
429 (valkyrie) or alignment (rogue), or race forces alignment (orc), &c */
432 /* Should we randomly pick for the player? */
433 if (ROLE == ROLE_NONE || RACE == ROLE_NONE || GEND == ROLE_NONE
434 || ALGN == ROLE_NONE) {
436 char *prompt = build_plselection_prompt(pbuf, QBUFSZ,
437 ROLE, RACE, GEND, ALGN);
439 /* this prompt string ends in "[ynaq]?":
440 y - game picks role,&c then asks player to confirm;
441 n - player manually chooses via menu selections;
442 a - like 'y', but skips confirmation and starts game;
445 tty_putstr(BASE_WINDOW, 0, "");
446 echoline = wins[BASE_WINDOW]->cury;
447 tty_putstr(BASE_WINDOW, 0, prompt);
449 pick4u = lowc(readchar());
450 if (index(quitchars, pick4u))
452 } while (!index(ynaqchars, pick4u));
453 if ((int) strlen(prompt) + 1 < CO) {
454 /* Echo choice and move back down line */
455 tty_putsym(BASE_WINDOW, (int) strlen(prompt) + 1, echoline,
457 tty_putstr(BASE_WINDOW, 0, "");
459 /* Otherwise it's hard to tell where to echo, and things are
460 * wrapping a bit messily anyway, so (try to) make sure the next
461 * question shows up well and doesn't get wrapped at the
462 * bottom of the window.
464 tty_clear_nhwindow(BASE_WINDOW);
466 if (pick4u != 'y' && pick4u != 'a' && pick4u != 'n')
473 if (nextpick == RS_ROLE) {
475 /* Select a role, if necessary;
476 we'll try to be compatible with pre-selected
477 race/gender/alignment, but may not succeed. */
479 /* Process the choice */
480 if (pick4u == 'y' || pick4u == 'a' || ROLE == ROLE_RANDOM) {
481 /* Pick a random role */
482 k = pick_role(RACE, GEND, ALGN, PICK_RANDOM);
484 tty_putstr(BASE_WINDOW, 0, "Incompatible role!");
488 /* Prompt for a role */
489 tty_clear_nhwindow(BASE_WINDOW);
490 role_selection_prolog(RS_ROLE, BASE_WINDOW);
491 win = create_nhwindow(NHW_MENU);
493 /* populate the menu with role choices */
494 setup_rolemenu(win, TRUE, RACE, GEND, ALGN);
495 /* add miscellaneous menu entries */
496 role_menu_extra(ROLE_RANDOM, win, TRUE);
497 any = zeroany; /* separator, not a choice */
498 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
500 role_menu_extra(RS_RACE, win, FALSE);
501 role_menu_extra(RS_GENDER, win, FALSE);
502 role_menu_extra(RS_ALGNMNT, win, FALSE);
504 role_menu_extra(RS_filter, win, FALSE);
505 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
507 Strcpy(pbuf, "Pick a role or profession");
509 Strcpy(pbuf, "
\90E
\8bÆ
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢");
511 n = select_menu(win, PICK_ONE, &selected);
513 * PICK_ONE with preselected choice behaves strangely:
514 * n == -1 -- <escape>, so use quit choice;
515 * n == 0 -- explicitly chose preselected entry,
516 * toggling it off, so use it;
517 * n == 1 -- implicitly chose preselected entry
518 * with <space> or <return>;
519 * n == 2 -- explicitly chose a different entry, so
520 * both it and preselected one are in list.
523 choice = selected[0].item.a_int;
524 if (n > 1 && choice == ROLE_RANDOM)
525 choice = selected[1].item.a_int;
527 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
529 free((genericptr_t) selected), selected = 0;
530 destroy_nhwindow(win);
532 if (choice == ROLE_NONE) {
533 goto give_up; /* Selected quit */
534 } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
535 ALGN = k = ROLE_NONE;
536 nextpick = RS_ALGNMNT;
537 } else if (choice == RS_menu_arg(RS_GENDER)) {
538 GEND = k = ROLE_NONE;
539 nextpick = RS_GENDER;
540 } else if (choice == RS_menu_arg(RS_RACE)) {
541 RACE = k = ROLE_NONE;
543 } else if (choice == RS_menu_arg(RS_filter)) {
544 ROLE = k = ROLE_NONE;
545 (void) reset_role_filtering();
547 } else if (choice == ROLE_RANDOM) {
548 k = pick_role(RACE, GEND, ALGN, PICK_RANDOM);
559 if (nextpick == RS_RACE) {
560 nextpick = (ROLE < 0) ? RS_ROLE : RS_GENDER;
561 /* Select a race, if necessary;
562 force compatibility with role, try for compatibility
563 with pre-selected gender/alignment. */
564 if (RACE < 0 || !validrace(ROLE, RACE)) {
565 /* no race yet, or pre-selected race not valid */
566 if (pick4u == 'y' || pick4u == 'a' || RACE == ROLE_RANDOM) {
567 k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM);
569 tty_putstr(BASE_WINDOW, 0, "Incompatible race!");
572 } else { /* pick4u == 'n' */
573 /* Count the number of valid races */
574 n = 0; /* number valid */
575 k = 0; /* valid race */
576 for (i = 0; races[i].noun; i++)
577 if (ok_race(ROLE, i, GEND, ALGN)) {
582 for (i = 0; races[i].noun; i++)
583 if (validrace(ROLE, i)) {
588 /* Permit the user to pick, if there is more than one */
590 tty_clear_nhwindow(BASE_WINDOW);
591 role_selection_prolog(RS_RACE, BASE_WINDOW);
592 win = create_nhwindow(NHW_MENU);
594 any = zeroany; /* zero out all bits */
595 /* populate the menu with role choices */
596 setup_racemenu(win, TRUE, ROLE, GEND, ALGN);
597 /* add miscellaneous menu entries */
598 role_menu_extra(ROLE_RANDOM, win, TRUE);
599 any.a_int = 0; /* separator, not a choice */
600 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
602 role_menu_extra(RS_ROLE, win, FALSE);
603 role_menu_extra(RS_GENDER, win, FALSE);
604 role_menu_extra(RS_ALGNMNT, win, FALSE);
606 role_menu_extra(RS_filter, win, FALSE);
607 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
609 Strcpy(pbuf, "Pick a race or species");
611 Strcpy(pbuf, "
\8eí
\91°
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢");
613 n = select_menu(win, PICK_ONE, &selected);
615 choice = selected[0].item.a_int;
616 if (n > 1 && choice == ROLE_RANDOM)
617 choice = selected[1].item.a_int;
619 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
621 free((genericptr_t) selected), selected = 0;
622 destroy_nhwindow(win);
624 if (choice == ROLE_NONE) {
625 goto give_up; /* Selected quit */
626 } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
627 ALGN = k = ROLE_NONE;
628 nextpick = RS_ALGNMNT;
629 } else if (choice == RS_menu_arg(RS_GENDER)) {
630 GEND = k = ROLE_NONE;
631 nextpick = RS_GENDER;
632 } else if (choice == RS_menu_arg(RS_ROLE)) {
633 ROLE = k = ROLE_NONE;
635 } else if (choice == RS_menu_arg(RS_filter)) {
636 RACE = k = ROLE_NONE;
637 if (reset_role_filtering())
641 } else if (choice == ROLE_RANDOM) {
642 k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM);
654 if (nextpick == RS_GENDER) {
655 nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE
657 /* Select a gender, if necessary;
658 force compatibility with role/race, try for compatibility
659 with pre-selected alignment. */
660 if (GEND < 0 || !validgend(ROLE, RACE, GEND)) {
661 /* no gender yet, or pre-selected gender not valid */
662 if (pick4u == 'y' || pick4u == 'a' || GEND == ROLE_RANDOM) {
663 k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM);
665 tty_putstr(BASE_WINDOW, 0, "Incompatible gender!");
666 k = randgend(ROLE, RACE);
668 } else { /* pick4u == 'n' */
669 /* Count the number of valid genders */
670 n = 0; /* number valid */
671 k = 0; /* valid gender */
672 for (i = 0; i < ROLE_GENDERS; i++)
673 if (ok_gend(ROLE, RACE, i, ALGN)) {
678 for (i = 0; i < ROLE_GENDERS; i++)
679 if (validgend(ROLE, RACE, i)) {
684 /* Permit the user to pick, if there is more than one */
686 tty_clear_nhwindow(BASE_WINDOW);
687 role_selection_prolog(RS_GENDER, BASE_WINDOW);
688 win = create_nhwindow(NHW_MENU);
690 any = zeroany; /* zero out all bits */
691 /* populate the menu with gender choices */
692 setup_gendmenu(win, TRUE, ROLE, RACE, ALGN);
693 /* add miscellaneous menu entries */
694 role_menu_extra(ROLE_RANDOM, win, TRUE);
695 any.a_int = 0; /* separator, not a choice */
696 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
698 role_menu_extra(RS_ROLE, win, FALSE);
699 role_menu_extra(RS_RACE, win, FALSE);
700 role_menu_extra(RS_ALGNMNT, win, FALSE);
702 role_menu_extra(RS_filter, win, FALSE);
703 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
705 Strcpy(pbuf, "Pick a gender or sex");
707 Strcpy(pbuf, "
\90«
\95Ê
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢");
709 n = select_menu(win, PICK_ONE, &selected);
711 choice = selected[0].item.a_int;
712 if (n > 1 && choice == ROLE_RANDOM)
713 choice = selected[1].item.a_int;
715 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
717 free((genericptr_t) selected), selected = 0;
718 destroy_nhwindow(win);
720 if (choice == ROLE_NONE) {
721 goto give_up; /* Selected quit */
722 } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
723 ALGN = k = ROLE_NONE;
724 nextpick = RS_ALGNMNT;
725 } else if (choice == RS_menu_arg(RS_RACE)) {
726 RACE = k = ROLE_NONE;
728 } else if (choice == RS_menu_arg(RS_ROLE)) {
729 ROLE = k = ROLE_NONE;
731 } else if (choice == RS_menu_arg(RS_filter)) {
732 GEND = k = ROLE_NONE;
733 if (reset_role_filtering())
736 nextpick = RS_GENDER;
737 } else if (choice == ROLE_RANDOM) {
738 k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM);
740 k = randgend(ROLE, RACE);
747 } /* needed gender */
748 } /* picking gender */
750 if (nextpick == RS_ALGNMNT) {
751 nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE : RS_GENDER;
752 /* Select an alignment, if necessary;
753 force compatibility with role/race/gender. */
754 if (ALGN < 0 || !validalign(ROLE, RACE, ALGN)) {
755 /* no alignment yet, or pre-selected alignment not valid */
756 if (pick4u == 'y' || pick4u == 'a' || ALGN == ROLE_RANDOM) {
757 k = pick_align(ROLE, RACE, GEND, PICK_RANDOM);
759 tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!");
760 k = randalign(ROLE, RACE);
762 } else { /* pick4u == 'n' */
763 /* Count the number of valid alignments */
764 n = 0; /* number valid */
765 k = 0; /* valid alignment */
766 for (i = 0; i < ROLE_ALIGNS; i++)
767 if (ok_align(ROLE, RACE, GEND, i)) {
772 for (i = 0; i < ROLE_ALIGNS; i++)
773 if (validalign(ROLE, RACE, i)) {
778 /* Permit the user to pick, if there is more than one */
780 tty_clear_nhwindow(BASE_WINDOW);
781 role_selection_prolog(RS_ALGNMNT, BASE_WINDOW);
782 win = create_nhwindow(NHW_MENU);
784 any = zeroany; /* zero out all bits */
785 setup_algnmenu(win, TRUE, ROLE, RACE, GEND);
786 role_menu_extra(ROLE_RANDOM, win, TRUE);
787 any.a_int = 0; /* separator, not a choice */
788 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
790 role_menu_extra(RS_ROLE, win, FALSE);
791 role_menu_extra(RS_RACE, win, FALSE);
792 role_menu_extra(RS_GENDER, win, FALSE);
794 role_menu_extra(RS_filter, win, FALSE);
795 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
797 Strcpy(pbuf, "Pick an alignment or creed");
799 Strcpy(pbuf, "
\91®
\90«
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢");
801 n = select_menu(win, PICK_ONE, &selected);
803 choice = selected[0].item.a_int;
804 if (n > 1 && choice == ROLE_RANDOM)
805 choice = selected[1].item.a_int;
807 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
809 free((genericptr_t) selected), selected = 0;
810 destroy_nhwindow(win);
812 if (choice == ROLE_NONE) {
813 goto give_up; /* Selected quit */
814 } else if (choice == RS_menu_arg(RS_GENDER)) {
815 GEND = k = ROLE_NONE;
816 nextpick = RS_GENDER;
817 } else if (choice == RS_menu_arg(RS_RACE)) {
818 RACE = k = ROLE_NONE;
820 } else if (choice == RS_menu_arg(RS_ROLE)) {
821 ROLE = k = ROLE_NONE;
823 } else if (choice == RS_menu_arg(RS_filter)) {
824 ALGN = k = ROLE_NONE;
825 if (reset_role_filtering())
828 nextpick = RS_ALGNMNT;
829 } else if (choice == ROLE_RANDOM) {
830 k = pick_align(ROLE, RACE, GEND, PICK_RANDOM);
832 k = randalign(ROLE, RACE);
839 } /* needed alignment */
840 } /* picking alignment */
842 } while (ROLE < 0 || RACE < 0 || GEND < 0 || ALGN < 0);
845 * Role, race, &c have now been determined;
846 * ask for confirmation and maybe go back to choose all over again.
848 * Uses ynaq for familiarity, although 'a' is usually a
849 * superset of 'y' but here is an alternate form of 'n'.
851 * title: Is this ok? [ynaq]
853 * text: $name, $alignment $gender $race $role
855 * menu: y + yes; play
857 * maybe: a - no; rename hero
861 getconfirmation = (picksomething && pick4u != 'a' && !flags.randomall);
862 while (getconfirmation) {
863 tty_clear_nhwindow(BASE_WINDOW);
864 role_selection_prolog(ROLE_NONE, BASE_WINDOW);
865 win = create_nhwindow(NHW_MENU);
867 any = zeroany; /* zero out all bits */
869 if (!roles[ROLE].name.f
870 && (roles[ROLE].allow & ROLE_GENDMASK)
871 == (ROLE_MALE | ROLE_FEMALE))
873 Sprintf(plbuf, " %s", genders[GEND].adj);
875 Sprintf(plbuf, "%s
\82Ì", genders[GEND].adj);
877 *plbuf = '\0'; /* omit redundant gender */
879 Sprintf(pbuf, "%s, %s%s %s %s", plname, aligns[ALGN].adj, plbuf,
881 (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f
882 : roles[ROLE].name.m);
884 Sprintf(pbuf, "%s, %s%s%s%s", plname, aligns[ALGN].adj, plbuf,
886 (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f
887 : roles[ROLE].name.m);
889 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, pbuf,
891 /* blank separator */
893 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
894 /* [ynaq] menu choices */
897 add_menu(win, NO_GLYPH, &any, 'y', 0, ATR_NONE, "Yes; start game",
899 add_menu(win, NO_GLYPH, &any, 'y', 0, ATR_NONE, "
\82Í
\82¢;
\83Q
\81[
\83\80\82ð
\8en
\82ß
\82é",
902 add_menu(win, NO_GLYPH, &any, 'n', 0, ATR_NONE,
904 "No; choose role again", MENU_UNSELECTED);
906 "
\82¢
\82¢
\82¦;
\90E
\8bÆ
\82ð
\91I
\82Ñ
\92¼
\82·", MENU_UNSELECTED);
907 if (iflags.renameallowed) {
909 add_menu(win, NO_GLYPH, &any, 'a', 0, ATR_NONE,
911 "Not yet; choose another name", MENU_UNSELECTED);
913 "
\82Ü
\82¾;
\96¼
\91O
\82ð
\95Ï
\82¦
\82é", MENU_UNSELECTED);
917 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
919 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "
\94²
\82¯
\82é",
922 Sprintf(pbuf, "Is this ok? [yn%sq]", iflags.renameallowed ? "a" : "");
924 Sprintf(pbuf, "
\82±
\82ê
\82Å
\82æ
\82¢
\81H [yn%sq]", iflags.renameallowed ? "a" : "");
926 n = select_menu(win, PICK_ONE, &selected);
927 /* [pick-one menus with a preselected entry behave oddly...] */
928 choice = (n > 0) ? selected[n - 1].item.a_int : (n == 0) ? 1 : -1;
930 free((genericptr_t) selected), selected = 0;
931 destroy_nhwindow(win);
934 default: /* 'q' or ESC */
935 goto give_up; /* quit */
939 * TODO: what, if anything, should be done if the name is
940 * changed to or from "wizard" after port-specific startup
941 * code has set flags.debug based on the original name?
943 int saveROLE, saveRACE, saveGEND, saveALGN;
945 iflags.renameinprogress = TRUE;
946 /* plnamesuffix() can change any or all of ROLE, RACE,
947 GEND, ALGN; we'll override that and honor only the name */
948 saveROLE = ROLE, saveRACE = RACE, saveGEND = GEND,
951 plnamesuffix(); /* calls askname() when plname[] is empty */
952 ROLE = saveROLE, RACE = saveRACE, GEND = saveGEND,
954 break; /* getconfirmation is still True */
957 /* start fresh, but bypass "shall I pick everything for you?"
958 step; any partial role selection via config file, command
959 line, or name suffix is discarded this time */
961 ROLE = RACE = GEND = ALGN = ROLE_NONE;
964 case 1: /* 'y' or Space or Return/Enter */
965 /* success; drop out through end of function */
966 getconfirmation = FALSE;
972 tty_display_nhwindow(BASE_WINDOW, FALSE);
978 free((genericptr_t) selected); /* [obsolete] */
985 reset_role_filtering()
990 menu_item *selected = 0;
992 win = create_nhwindow(NHW_MENU);
996 /* no extra blank line preceding this entry; end_menu supplies one */
997 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
998 "Unacceptable roles", MENU_UNSELECTED);
999 setup_rolemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
1001 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1002 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
1003 "Unacceptable races", MENU_UNSELECTED);
1004 setup_racemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
1006 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1007 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
1008 "Unacceptable genders", MENU_UNSELECTED);
1009 setup_gendmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
1011 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1012 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
1013 "Uncceptable alignments", MENU_UNSELECTED);
1014 setup_algnmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
1016 end_menu(win, "Pick all that apply");
1017 n = select_menu(win, PICK_ANY, &selected);
1021 for (i = 0; i < n; i++)
1022 setrolefilter(selected[i].item.a_string);
1024 ROLE = RACE = GEND = ALGN = ROLE_NONE;
1027 free((genericptr_t) selected), selected = 0;
1028 destroy_nhwindow(win);
1029 return (n > 0) ? TRUE : FALSE;
1037 /* add entries a-Archeologist, b-Barbarian, &c to menu being built in 'win' */
1039 setup_rolemenu(win, filtering, race, gend, algn)
1041 boolean filtering; /* True => exclude filtered roles; False => filter reset */
1042 int race, gend, algn; /* all ROLE_NONE for !filtering case */
1047 char thisch, lastch = '\0', rolenamebuf[50];
1049 any = zeroany; /* zero out all bits */
1050 for (i = 0; roles[i].name.m; i++) {
1051 role_ok = ok_role(i, race, gend, algn);
1052 if (filtering && !role_ok)
1057 any.a_string = roles[i].name.m;
1059 thisch = lowc(*roles[i].name.m);
1061 thisch = lowc(*roles[i].filecode);
1062 if (thisch == lastch)
1063 thisch = highc(thisch);
1064 Strcpy(rolenamebuf, roles[i].name.m);
1065 if (roles[i].name.f) {
1066 /* role has distinct name for female (C,P) */
1068 /* female already chosen; replace male name */
1069 Strcpy(rolenamebuf, roles[i].name.f);
1070 } else if (gend < 0) {
1071 /* not chosen yet; append slash+female name */
1072 Strcat(rolenamebuf, "/");
1073 Strcat(rolenamebuf, roles[i].name.f);
1076 /* !filtering implies reset_role_filtering() where we want to
1077 mark this role as preseleted if current filter excludes it */
1078 add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE, an(rolenamebuf),
1079 (!filtering && !role_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1085 setup_racemenu(win, filtering, role, gend, algn)
1088 int role, gend, algn;
1096 for (i = 0; races[i].noun; i++) {
1097 race_ok = ok_race(role, i, gend, algn);
1098 if (filtering && !race_ok)
1103 any.a_string = races[i].noun;
1105 this_ch = *races[i].noun;
1107 this_ch = lowc(*races[i].filecode);
1108 /* filtering: picking race, so choose by first letter, with
1109 capital letter as unseen accelerator;
1110 !filtering: resetting filter rather than picking, choose by
1111 capital letter since lowercase role letters will be present */
1112 add_menu(win, NO_GLYPH, &any,
1113 filtering ? this_ch : highc(this_ch),
1114 filtering ? highc(this_ch) : 0,
1115 ATR_NONE, races[i].noun,
1116 (!filtering && !race_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1121 setup_gendmenu(win, filtering, role, race, algn)
1124 int role, race, algn;
1132 for (i = 0; i < ROLE_GENDERS; i++) {
1133 gend_ok = ok_gend(role, race, i, algn);
1134 if (filtering && !gend_ok)
1139 any.a_string = genders[i].adj;
1141 this_ch = *genders[i].adj;
1143 this_ch = lowc(*genders[i].filecode);
1144 /* (see setup_racemenu for explanation of selector letters
1145 and setup_rolemenu for preselection) */
1146 add_menu(win, NO_GLYPH, &any,
1147 filtering ? this_ch : highc(this_ch),
1148 filtering ? highc(this_ch) : 0,
1149 ATR_NONE, genders[i].adj,
1150 (!filtering && !gend_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1155 setup_algnmenu(win, filtering, role, race, gend)
1158 int role, race, gend;
1166 for (i = 0; i < ROLE_ALIGNS; i++) {
1167 algn_ok = ok_align(role, race, gend, i);
1168 if (filtering && !algn_ok)
1174 any.a_string = aligns[i].adj;
1176 any.a_string = aligns[i].noun;
1178 this_ch = *aligns[i].adj;
1180 this_ch = lowc(*aligns[i].filecode);
1181 /* (see setup_racemenu for explanation of selector letters
1182 and setup_rolemenu for preselection) */
1184 add_menu(win, NO_GLYPH, &any,
1185 filtering ? this_ch : highc(this_ch),
1186 filtering ? highc(this_ch) : 0,
1187 ATR_NONE, aligns[i].adj,
1188 (!filtering && !algn_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1190 add_menu(win, NO_GLYPH, &any,
1191 filtering ? this_ch : highc(this_ch),
1192 filtering ? highc(this_ch) : 0,
1193 ATR_NONE, aligns[i].noun,
1194 (!filtering && !algn_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1200 * plname is filled either by an option (-u Player or -uPlayer) or
1201 * explicitly (by being the wizard) or by askname.
1202 * It may still contain a suffix denoting the role, etc.
1203 * Always called after init_nhwindows() and before display_gamewindows().
1209 static const char who_are_you[] = "Who are you? ";
1211 static const char who_are_you[] = "
\82 \82È
\82½
\82Í
\92N
\81H ";
1212 register int c, ct, tryct = 0;
1214 char ptmpname[PL_NSIZ];
1218 if (iflags.wc2_selectsaved && !iflags.renameinprogress)
1219 switch (restore_menu(BASE_WINDOW)) {
1221 bail("Until next time then..."); /* quit */
1224 break; /* no game chosen; start new game */
1226 return; /* plname[] has been set */
1228 #endif /* SELECTSAVED */
1230 tty_putstr(BASE_WINDOW, 0, "");
1234 bail("Giving up after 10 tries.\n");
1235 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury - 1);
1237 tty_putstr(BASE_WINDOW, 0, "Enter a name for your character...");
1239 tty_putstr(BASE_WINDOW, 0, "
\82 \82È
\82½
\82Ì
\83L
\83\83\83\89\83N
\83^
\82Ì
\96¼
\91O
\82Í
\81H");
1240 /* erase previous prompt (in case of ESC after partial response)
1242 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury), cl_end();
1244 tty_putstr(BASE_WINDOW, 0, who_are_you);
1245 tty_curs(BASE_WINDOW, (int) (sizeof who_are_you),
1246 wins[BASE_WINDOW]->cury - 1);
1248 while ((c = tty_nhgetch()) != '\n') {
1256 } /* continue outer loop */
1257 #if defined(WIN32CON)
1259 bail("^C abort.\n");
1261 /* some people get confused when their erase char is not ^H */
1262 if (c == '\b' || c == '\177') {
1271 #if defined(MICRO) || defined(WIN32CON)
1272 #if defined(WIN32CON) || defined(MSDOS)
1273 backsp(); /* \b is visible on NT */
1274 (void) putchar(' ');
1280 (void) putchar('\b');
1281 (void) putchar(' ');
1282 (void) putchar('\b');
1286 if(is_kanji2(ptmpname, ct))
1291 #if defined(UNIX) || defined(VMS)
1293 if (c != '-' && c != '@')
1294 if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') &&
1295 /* reject leading digit but allow digits elsewhere
1296 (avoids ambiguity when character name gets
1297 appended to uid to construct save file name) */
1298 !(c >= '0' && c <= '9' && ct > 0))
1302 if (ct < (int) (sizeof plname) - 1) {
1305 if (iflags.grmode) {
1312 wins[BASE_WINDOW]->curx++;
1341 /* move to next line to simulate echo of user's <return> */
1342 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury + 1);
1344 Strcpy(plname, str2ic(ptmpname));
1347 /* since we let user pick an arbitrary name now, he/she can pick
1348 another one during role selection */
1349 iflags.renameallowed = TRUE;
1358 #if !defined(MICRO) && !defined(WIN32CON)
1369 xputs(iflags.cbreak ? "space" : "return");
1370 xputs(" to continue: ");
1378 tty_suspend_nhwindows(str)
1381 settty(str); /* calls end_screen, perhaps raw_print */
1383 tty_raw_print(""); /* calls fflush(stdout) */
1387 tty_resume_nhwindows()
1390 setftty(); /* calls start_screen */
1395 tty_exit_nhwindows(str)
1400 tty_suspend_nhwindows(str);
1402 * Disable windows to avoid calls to window routines.
1404 free_pickinv_cache(); /* reset its state as well as tear down window */
1405 for (i = 0; i < MAXWIN; i++) {
1406 if (i == BASE_WINDOW)
1407 continue; /* handle wins[BASE_WINDOW] last */
1409 #ifdef FREE_ALL_MEMORY
1410 free_window_info(wins[i], TRUE);
1411 free((genericptr_t) wins[i]);
1413 wins[i] = (struct WinDesc *) 0;
1416 WIN_MAP = WIN_MESSAGE = WIN_INVEN = WIN_ERR; /* these are all gone now */
1417 WIN_STATUS = WIN_ERR;
1418 #ifdef FREE_ALL_MEMORY
1419 if (BASE_WINDOW != WIN_ERR && wins[BASE_WINDOW]) {
1420 free_window_info(wins[BASE_WINDOW], TRUE);
1421 free((genericptr_t) wins[BASE_WINDOW]);
1422 wins[BASE_WINDOW] = (struct WinDesc *) 0;
1423 BASE_WINDOW = WIN_ERR;
1425 free((genericptr_t) ttyDisplay);
1426 ttyDisplay = (struct DisplayDesc *) 0;
1429 #ifndef NO_TERMS /*(until this gets added to the window interface)*/
1430 tty_shutdown(); /* cleanup termcap/terminfo/whatever */
1432 iflags.window_inited = 0;
1436 tty_create_nhwindow(type)
1439 struct WinDesc *newwin;
1443 if (maxwin == MAXWIN)
1446 newwin = (struct WinDesc *) alloc(sizeof(struct WinDesc));
1447 newwin->type = type;
1449 newwin->active = FALSE;
1450 newwin->curx = newwin->cury = 0;
1451 newwin->morestr = 0;
1452 newwin->mlist = (tty_menu_item *) 0;
1453 newwin->plist = (tty_menu_item **) 0;
1454 newwin->npages = newwin->plist_size = newwin->nitems = newwin->how = 0;
1457 /* base window, used for absolute movement on the screen */
1458 newwin->offx = newwin->offy = 0;
1459 newwin->rows = ttyDisplay->rows;
1460 newwin->cols = ttyDisplay->cols;
1461 newwin->maxrow = newwin->maxcol = 0;
1464 /* message window, 1 line long, very wide, top of screen */
1465 newwin->offx = newwin->offy = 0;
1467 if (iflags.msg_history < 20)
1468 iflags.msg_history = 20;
1469 else if (iflags.msg_history > 60)
1470 iflags.msg_history = 60;
1471 newwin->maxrow = newwin->rows = iflags.msg_history;
1472 newwin->maxcol = newwin->cols = 0;
1475 /* status window, 2 lines long, full width, bottom of screen */
1477 #if defined(USE_TILES) && defined(MSDOS)
1478 if (iflags.grmode) {
1479 newwin->offy = ttyDisplay->rows - 2;
1482 newwin->offy = min((int) ttyDisplay->rows - 2, ROWNO + 1);
1483 newwin->rows = newwin->maxrow = 2;
1484 newwin->cols = newwin->maxcol = ttyDisplay->cols;
1487 /* map window, ROWNO lines long, full width, below message window */
1490 newwin->rows = ROWNO;
1491 newwin->cols = COLNO;
1492 newwin->maxrow = 0; /* no buffering done -- let gbuf do it */
1497 /* inventory/menu window, variable length, full width, top of screen
1499 /* help window, the same, different semantics for display, etc */
1500 newwin->offx = newwin->offy = 0;
1502 newwin->cols = ttyDisplay->cols;
1503 newwin->maxrow = newwin->maxcol = 0;
1506 panic("Tried to create window type %d\n", (int) type);
1510 for (newid = 0; newid < MAXWIN; newid++) {
1511 if (wins[newid] == 0) {
1512 wins[newid] = newwin;
1516 if (newid == MAXWIN) {
1517 panic("No window slots!");
1521 if (newwin->maxrow) {
1523 (char **) alloc(sizeof(char *) * (unsigned) newwin->maxrow);
1525 (short *) alloc(sizeof(short) * (unsigned) newwin->maxrow);
1526 if (newwin->maxcol) {
1528 for (i = 0; i < newwin->maxrow; i++) {
1529 newwin->data[i] = (char *) alloc((unsigned) newwin->maxcol);
1530 newwin->datlen[i] = (short) newwin->maxcol;
1533 for (i = 0; i < newwin->maxrow; i++) {
1534 newwin->data[i] = (char *) 0;
1535 newwin->datlen[i] = 0;
1538 if (newwin->type == NHW_MESSAGE)
1541 newwin->data = (char **) 0;
1542 newwin->datlen = (short *) 0;
1549 erase_menu_or_text(window, cw, clear)
1556 tty_curs(window, 1, 0);
1563 docorner((int) cw->offx, cw->maxrow + 1);
1567 free_window_info(cw, free_data)
1574 if (cw == wins[WIN_MESSAGE] && cw->rows > cw->maxrow)
1575 cw->maxrow = cw->rows; /* topl data */
1576 for (i = 0; i < cw->maxrow; i++)
1578 free((genericptr_t) cw->data[i]);
1579 cw->data[i] = (char *) 0;
1584 free((genericptr_t) cw->data);
1585 cw->data = (char **) 0;
1587 free((genericptr_t) cw->datlen);
1588 cw->datlen = (short *) 0;
1592 cw->maxrow = cw->maxcol = 0;
1594 tty_menu_item *temp;
1596 while ((temp = cw->mlist) != 0) {
1597 cw->mlist = cw->mlist->next;
1599 free((genericptr_t) temp->str);
1600 free((genericptr_t) temp);
1604 free((genericptr_t) cw->plist);
1607 cw->plist_size = cw->npages = cw->nitems = cw->how = 0;
1609 free((genericptr_t) cw->morestr);
1615 tty_clear_nhwindow(window)
1618 register struct WinDesc *cw = 0;
1620 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1621 panic(winpanicstr, window);
1622 ttyDisplay->lastwin = window;
1624 print_vt_code2(AVTC_SELECT_WINDOW, window);
1628 if (ttyDisplay->toplin) {
1632 docorner(1, cw->cury + 1);
1633 ttyDisplay->toplin = 0;
1637 tty_curs(window, 1, 0);
1639 tty_curs(window, 1, 1);
1643 /* cheap -- clear the whole thing and tell nethack to redraw botl */
1652 erase_menu_or_text(window, cw, TRUE);
1653 free_window_info(cw, FALSE);
1656 cw->curx = cw->cury = 0;
1660 toggle_menu_curr(window, curr, lineno, in_view, counting, count)
1662 tty_menu_item *curr;
1664 boolean in_view, counting;
1667 if (curr->selected) {
1668 if (counting && count > 0) {
1669 curr->count = count;
1671 set_item_state(window, lineno, curr);
1673 } else { /* change state */
1674 curr->selected = FALSE;
1677 set_item_state(window, lineno, curr);
1680 } else { /* !selected */
1681 if (counting && count > 0) {
1682 curr->count = count;
1683 curr->selected = TRUE;
1685 set_item_state(window, lineno, curr);
1687 } else if (!counting) {
1688 curr->selected = TRUE;
1690 set_item_state(window, lineno, curr);
1693 /* do nothing counting&&count==0 */
1700 register struct WinDesc *cw;
1701 const char *s; /* valid responses */
1703 const char *prompt = cw->morestr ? cw->morestr : defmorestr;
1704 int offset = (cw->type == NHW_TEXT) ? 1 : 2;
1706 tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + offset,
1707 (int) ttyDisplay->cury);
1712 ttyDisplay->curx += strlen(prompt);
1714 jputchar('\0'); /* reset */
1732 set_item_state(window, lineno, item)
1735 tty_menu_item *item;
1737 char ch = item->selected ? (item->count == -1L ? '+' : '#') : '-';
1739 tty_curs(window, 4, lineno);
1740 term_start_attr(item->attr);
1744 (void) jputchar(ch);
1746 term_end_attr(item->attr);
1750 set_all_on_page(window, page_start, page_end)
1752 tty_menu_item *page_start, *page_end;
1754 tty_menu_item *curr;
1757 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1758 if (curr->identifier.a_void && !curr->selected) {
1759 curr->selected = TRUE;
1760 set_item_state(window, n, curr);
1765 unset_all_on_page(window, page_start, page_end)
1767 tty_menu_item *page_start, *page_end;
1769 tty_menu_item *curr;
1772 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1773 if (curr->identifier.a_void && curr->selected) {
1774 curr->selected = FALSE;
1776 set_item_state(window, n, curr);
1781 invert_all_on_page(window, page_start, page_end, acc)
1783 tty_menu_item *page_start, *page_end;
1784 char acc; /* group accelerator, 0 => all */
1786 tty_menu_item *curr;
1789 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1790 if (curr->identifier.a_void && (acc == 0 || curr->gselector == acc)) {
1791 if (curr->selected) {
1792 curr->selected = FALSE;
1795 curr->selected = TRUE;
1796 set_item_state(window, n, curr);
1801 * Invert all entries that match the give group accelerator (or all if zero).
1804 invert_all(window, page_start, page_end, acc)
1806 tty_menu_item *page_start, *page_end;
1807 char acc; /* group accelerator, 0 => all */
1809 tty_menu_item *curr;
1810 boolean on_curr_page;
1811 struct WinDesc *cw = wins[window];
1813 invert_all_on_page(window, page_start, page_end, acc);
1815 /* invert the rest */
1816 for (on_curr_page = FALSE, curr = cw->mlist; curr; curr = curr->next) {
1817 if (curr == page_start)
1818 on_curr_page = TRUE;
1819 else if (curr == page_end)
1820 on_curr_page = FALSE;
1822 if (!on_curr_page && curr->identifier.a_void
1823 && (acc == 0 || curr->gselector == acc)) {
1824 if (curr->selected) {
1825 curr->selected = FALSE;
1828 curr->selected = TRUE;
1833 /* support menucolor in addition to caller-supplied attribute */
1835 toggle_menu_attr(on, color, attr)
1840 term_start_attr(attr);
1842 if (color != NO_COLOR)
1843 term_start_color(color);
1847 if (color != NO_COLOR)
1850 term_end_attr(attr);
1859 process_menu_window(window, cw)
1863 tty_menu_item *page_start, *page_end, *curr;
1865 int n, attr_n, curr_page, page_lines, resp_len;
1866 boolean finished, counting, reset_count;
1867 char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ], *msave, *morestr, really_morc;
1868 #define MENU_EXPLICIT_CHOICE 0x7f /* pseudo menu manipulation char */
1870 curr_page = page_lines = 0;
1871 page_start = page_end = 0;
1872 msave = cw->morestr; /* save the morestr */
1873 cw->morestr = morestr = (char *) alloc((unsigned) QBUFSZ);
1879 /* collect group accelerators; for PICK_NONE, they're ignored;
1880 for PICK_ONE, only those which match exactly one entry will be
1881 accepted; for PICK_ANY, those which match any entry are okay */
1883 if (cw->how != PICK_NONE) {
1885 #define GSELIDX(c) (c & 127) /* guard against `signed char' */
1887 for (i = 0; i < SIZE(gcnt); i++)
1889 for (n = 0, curr = cw->mlist; curr; curr = curr->next)
1890 if (curr->gselector && curr->gselector != curr->selector) {
1892 ++gcnt[GSELIDX(curr->gselector)];
1895 if (n > 0) /* at least one group accelerator found */
1896 for (rp = gacc, curr = cw->mlist; curr; curr = curr->next)
1897 if (curr->gselector && curr->gselector != curr->selector
1898 && !index(gacc, curr->gselector)
1899 && (cw->how == PICK_ANY
1900 || gcnt[GSELIDX(curr->gselector)] == 1)) {
1901 *rp++ = curr->gselector;
1902 *rp = '\0'; /* re-terminate for index() */
1905 resp_len = 0; /* lint suppression */
1907 /* loop until finished */
1916 /* new page to be displayed */
1917 if (curr_page < 0 || (cw->npages > 0 && curr_page >= cw->npages))
1918 panic("bad menu screen page #%d", curr_page);
1921 if (!cw->offx) { /* if not corner, do clearscreen */
1923 tty_curs(window, 1, 0);
1930 if (cw->npages > 0) {
1931 /* collect accelerators */
1932 page_start = cw->plist[curr_page];
1933 page_end = cw->plist[curr_page + 1];
1934 for (page_lines = 0, curr = page_start; curr != page_end;
1935 page_lines++, curr = curr->next) {
1936 int attr, color = NO_COLOR;
1939 *rp++ = curr->selector;
1941 tty_curs(window, 1, page_lines);
1946 (void) putchar(' ');
1948 (void) jputchar(' ');
1952 if (!iflags.use_menu_color
1953 || !get_menu_coloring(curr->str, &color, &attr))
1956 /* which character to start attribute highlighting;
1957 whole line for headers and such, after the selector
1958 character and space and selection indicator for menu
1959 lines (including fake ones that simulate grayed-out
1960 entries, so we don't rely on curr->identifier here) */
1961 attr_n = 0; /* whole line */
1962 if (curr->str[0] && curr->str[1] == ' '
1963 && curr->str[2] && index("-+#", curr->str[2])
1964 && curr->str[3] == ' ')
1965 /* [0]=letter, [1]==space, [2]=[-+#], [3]=space */
1966 attr_n = 4; /* [4:N]=entry description */
1969 * Don't use xputs() because (1) under unix it calls
1970 * tputstr() which will interpret a '*' as some kind
1971 * of padding information and (2) it calls xputc to
1972 * actually output the character. We're faster doing
1975 for (n = 0, cp = curr->str;
1978 (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
1980 (int) ttyDisplay->curx < (int) ttyDisplay->cols;
1984 if (n == attr_n && (color != NO_COLOR
1985 || attr != ATR_NONE))
1986 toggle_menu_attr(TRUE, color, attr);
1988 && curr->identifier.a_void != 0
1989 && curr->selected) {
1990 if (curr->count == -1L)
1992 (void) putchar('+'); /* all selected */
1994 (void) jputchar('+'); /* all selected */
1998 (void) putchar('#'); /* count selected */
2000 (void) jputchar('#'); /* count selected */
2004 (void) putchar(*cp);
2006 (void) jputchar(*cp);
2009 if (n > attr_n && (color != NO_COLOR || attr != ATR_NONE))
2010 toggle_menu_attr(FALSE, color, attr);
2011 } /* if npages > 0 */
2018 /* remember how many explicit menu choices there are */
2019 resp_len = (int) strlen(resp);
2021 /* corner window - clear extra lines from last page */
2023 for (n = page_lines + 1; n < cw->maxrow; n++) {
2024 tty_curs(window, 1, n);
2029 /* set extra chars.. */
2030 Strcat(resp, default_menu_cmds);
2031 Strcat(resp, " "); /* next page or end */
2032 Strcat(resp, "0123456789\033\n\r"); /* counts, quit */
2033 Strcat(resp, gacc); /* group accelerators */
2034 Strcat(resp, mapped_menu_cmds);
2038 Sprintf(cw->morestr, "(%d of %d)", curr_page + 1,
2041 Sprintf(cw->morestr, "(%d/%d)", curr_page + 1,
2045 Strcpy(cw->morestr, msave);
2047 Strcpy(cw->morestr, defmorestr);
2049 tty_curs(window, 1, page_lines);
2053 /* just put the cursor back... */
2054 tty_curs(window, (int) strlen(cw->morestr) + 2, page_lines);
2055 xwaitforspace(resp);
2058 really_morc = morc; /* (only used with MENU_EXPLICIT_CHOICE */
2059 if ((rp = index(resp, morc)) != 0 && rp < resp + resp_len)
2060 /* explicit menu selection; don't override it if it also
2061 happens to match a mapped menu command (such as ':' to
2062 look inside a container vs ':' to search) */
2063 morc = MENU_EXPLICIT_CHOICE;
2065 morc = map_menu_cmd(morc);
2069 /* special case: '0' is also the default ball class */
2070 if (!counting && index(gacc, morc))
2072 /* fall through to count the zero */
2083 count = (count * 10L) + (long) (morc - '0');
2085 * It is debatable whether we should allow 0 to
2086 * start a count. There is no difference if the
2087 * item is selected. If not selected, then
2090 * count starting zero: "zero b's"
2091 * ignore starting zero: "select b"
2093 * At present I don't know which is better.
2095 if (count != 0L) { /* ignore leading zeros */
2097 reset_count = FALSE;
2100 case '\033': /* cancel - from counting or loop */
2102 /* deselect everything */
2103 for (curr = cw->mlist; curr; curr = curr->next) {
2104 curr->selected = FALSE;
2107 cw->flags |= WIN_CANCELLED;
2110 /* else only stop count */
2112 case '\0': /* finished (commit) */
2115 /* only finished if we are actually picking something */
2116 if (cw->how != PICK_NONE) {
2120 /* else fall through */
2122 case MENU_NEXT_PAGE:
2123 if (cw->npages > 0 && curr_page != cw->npages - 1) {
2126 } else if (morc == ' ') {
2127 /* ' ' finishes menus here, but stop '>' doing the same. */
2131 case MENU_PREVIOUS_PAGE:
2132 if (cw->npages > 0 && curr_page != 0) {
2137 case MENU_FIRST_PAGE:
2138 if (cw->npages > 0 && curr_page != 0) {
2143 case MENU_LAST_PAGE:
2144 if (cw->npages > 0 && curr_page != cw->npages - 1) {
2146 curr_page = cw->npages - 1;
2149 case MENU_SELECT_PAGE:
2150 if (cw->how == PICK_ANY)
2151 set_all_on_page(window, page_start, page_end);
2153 case MENU_UNSELECT_PAGE:
2154 unset_all_on_page(window, page_start, page_end);
2156 case MENU_INVERT_PAGE:
2157 if (cw->how == PICK_ANY)
2158 invert_all_on_page(window, page_start, page_end, 0);
2160 case MENU_SELECT_ALL:
2161 if (cw->how == PICK_ANY) {
2162 set_all_on_page(window, page_start, page_end);
2164 for (curr = cw->mlist; curr; curr = curr->next)
2165 if (curr->identifier.a_void && !curr->selected)
2166 curr->selected = TRUE;
2169 case MENU_UNSELECT_ALL:
2170 unset_all_on_page(window, page_start, page_end);
2171 /* unset the rest */
2172 for (curr = cw->mlist; curr; curr = curr->next)
2173 if (curr->identifier.a_void && curr->selected) {
2174 curr->selected = FALSE;
2178 case MENU_INVERT_ALL:
2179 if (cw->how == PICK_ANY)
2180 invert_all(window, page_start, page_end, 0);
2183 if (cw->how == PICK_NONE) {
2187 char searchbuf[BUFSZ + 2], tmpbuf[BUFSZ];
2188 boolean on_curr_page = FALSE;
2191 tty_getlin("Search for:", tmpbuf);
2192 if (!tmpbuf[0] || tmpbuf[0] == '\033')
2194 Sprintf(searchbuf, "*%s*", tmpbuf);
2196 for (curr = cw->mlist; curr; curr = curr->next) {
2199 if (curr == page_start)
2200 on_curr_page = TRUE;
2201 else if (curr == page_end)
2202 on_curr_page = FALSE;
2203 if (curr->identifier.a_void
2204 && pmatchi(searchbuf, curr->str)) {
2205 toggle_menu_curr(window, curr, lineno, on_curr_page,
2207 if (cw->how == PICK_ONE) {
2215 case MENU_EXPLICIT_CHOICE:
2219 if (cw->how == PICK_NONE || !index(resp, morc)) {
2220 /* unacceptable input received */
2223 } else if (index(gacc, morc)) {
2225 /* group accelerator; for the PICK_ONE case, we know that
2226 it matches exactly one item in order to be in gacc[] */
2227 invert_all(window, page_start, page_end, morc);
2228 if (cw->how == PICK_ONE)
2232 /* find, toggle, and possibly update */
2233 for (n = 0, curr = page_start; curr != page_end;
2234 n++, curr = curr->next)
2235 if (morc == curr->selector) {
2236 toggle_menu_curr(window, curr, n, TRUE, counting, count);
2237 if (cw->how == PICK_ONE)
2239 break; /* from `for' loop */
2245 cw->morestr = msave;
2246 free((genericptr_t) morestr);
2250 process_text_window(window, cw)
2257 for (n = 0, i = 0; i < cw->maxrow; i++) {
2258 if (!cw->offx && (n + cw->offy == ttyDisplay->rows - 1)) {
2259 tty_curs(window, 1, n);
2261 dmore(cw, quitchars);
2262 if (morc == '\033') {
2263 cw->flags |= WIN_CANCELLED;
2267 tty_curs(window, 1, 0);
2273 tty_curs(window, 1, n++);
2281 attr = cw->data[i][0] - 1;
2284 (void) putchar(' ');
2286 (void) jputchar(' ');
2290 term_start_attr(attr);
2291 for (cp = &cw->data[i][1];
2293 *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
2296 *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
2297 cp++, ttyDisplay->curx++)
2300 (void) putchar(*cp);
2302 (void) jputchar(*cp);
2304 term_end_attr(attr);
2307 if (i == cw->maxrow) {
2309 if (cw->type == NHW_TEXT) {
2310 tty_curs(BASE_WINDOW, 1, (int) ttyDisplay->cury + 1);
2314 tty_curs(BASE_WINDOW, (int) cw->offx + 1,
2315 (cw->type == NHW_TEXT) ? (int) ttyDisplay->rows - 1 : n);
2317 dmore(cw, quitchars);
2319 cw->flags |= WIN_CANCELLED;
2325 tty_display_nhwindow(window, blocking)
2327 boolean blocking; /* with ttys, all windows are blocking */
2329 register struct WinDesc *cw = 0;
2332 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2333 panic(winpanicstr, window);
2334 if (cw->flags & WIN_CANCELLED)
2336 ttyDisplay->lastwin = window;
2337 ttyDisplay->rawprint = 0;
2339 print_vt_code2(AVTC_SELECT_WINDOW, window);
2343 if (ttyDisplay->toplin == 1) {
2345 ttyDisplay->toplin = 1; /* more resets this */
2346 tty_clear_nhwindow(window);
2348 ttyDisplay->toplin = 0;
2349 cw->curx = cw->cury = 0;
2351 iflags.window_inited = TRUE;
2356 if (!ttyDisplay->toplin)
2357 ttyDisplay->toplin = 1;
2358 tty_display_nhwindow(WIN_MESSAGE, TRUE);
2363 (void) fflush(stdout);
2366 cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
2370 /* cw->maxcol is a long, but its value is constrained to
2371 be <= ttyDisplay->cols, so is sure to fit within a short */
2372 s_maxcol = (short) cw->maxcol;
2374 cw->offx = (cw->type == NHW_TEXT)
2376 : min(min(82, ttyDisplay->cols / 2),
2377 ttyDisplay->cols - s_maxcol - 1);
2379 /* avoid converting to uchar before calculations are finished */
2380 cw->offx = (uchar) max((int) 10,
2381 (int) (ttyDisplay->cols - s_maxcol - 1));
2385 if (cw->type == NHW_MENU)
2387 if (ttyDisplay->toplin == 1)
2388 tty_display_nhwindow(WIN_MESSAGE, TRUE);
2390 if (cw->maxrow >= (int) ttyDisplay->rows
2391 || !iflags.menu_overlay)
2393 if (cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows
2394 || !iflags.menu_overlay)
2398 if (cw->offy || iflags.menu_overlay) {
2399 tty_curs(window, 1, 0);
2403 ttyDisplay->toplin = 0;
2405 if (WIN_MESSAGE != WIN_ERR)
2406 tty_clear_nhwindow(WIN_MESSAGE);
2409 if (cw->data || !cw->maxrow)
2410 process_text_window(window, cw);
2412 process_menu_window(window, cw);
2419 tty_dismiss_nhwindow(window)
2422 register struct WinDesc *cw = 0;
2424 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2425 panic(winpanicstr, window);
2427 print_vt_code2(AVTC_SELECT_WINDOW, window);
2431 if (ttyDisplay->toplin)
2432 tty_display_nhwindow(WIN_MESSAGE, TRUE);
2438 * these should only get dismissed when the game is going away
2441 tty_curs(BASE_WINDOW, 1, (int) ttyDisplay->rows - 1);
2447 if (iflags.window_inited) {
2448 /* otherwise dismissing the text endwin after other windows
2449 * are dismissed tries to redraw the map and panics. since
2450 * the whole reason for dismissing the other windows was to
2451 * leave the ending window on the screen, we don't want to
2454 erase_menu_or_text(window, cw, FALSE);
2464 tty_destroy_nhwindow(window)
2467 register struct WinDesc *cw = 0;
2469 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2470 panic(winpanicstr, window);
2473 tty_dismiss_nhwindow(window);
2474 if (cw->type == NHW_MESSAGE)
2475 iflags.window_inited = 0;
2476 if (cw->type == NHW_MAP)
2479 free_window_info(cw, TRUE);
2480 free((genericptr_t) cw);
2485 tty_curs(window, x, y)
2487 register int x, y; /* not xchar: perhaps xchar is unsigned and
2488 curx-x would be unsigned as well */
2490 struct WinDesc *cw = 0;
2491 int cx = ttyDisplay->curx;
2492 int cy = ttyDisplay->cury;
2494 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2495 panic(winpanicstr, window);
2496 ttyDisplay->lastwin = window;
2498 print_vt_code2(AVTC_SELECT_WINDOW, window);
2500 #if defined(USE_TILES) && defined(MSDOS)
2501 adjust_cursor_flags(cw);
2503 cw->curx = --x; /* column 0 is never used */
2506 if (x < 0 || y < 0 || y >= cw->rows || x > cw->cols) {
2507 const char *s = "[unknown type]";
2510 s = "[topl window]";
2513 s = "[status window]";
2519 s = "[corner window]";
2522 s = "[text window]";
2525 s = "[base window]";
2528 debugpline4("bad curs positioning win %d %s (%d,%d)", window, s, x,
2530 /* This return statement caused a functional difference between DEBUG and
2531 non-DEBUG operation, so it is being commented out. It caused tty_curs()
2532 to fail to move the cursor to the location it needed to be if the x,y
2533 range checks failed, leaving the next piece of output to be displayed
2534 at whatever random location the cursor happened to be at prior. */
2543 if (clipping && window == WIN_MAP) {
2549 if (y == cy && x == cx)
2552 if (cw->type == NHW_MAP)
2556 if (!nh_ND && (cx != x || x <= 3)) { /* Extremely primitive */
2557 cmov(x, y); /* bunker!wtm */
2566 if (cy <= 3 && cx <= 3) {
2569 } else if ((x <= 3 && cy <= 3) || (!nh_CM && x < cx)) {
2571 (void) putchar('\r');
2573 (void) cputchar('\r');
2575 ttyDisplay->curx = 0;
2577 } else if (!nh_CM) {
2583 ttyDisplay->curx = x;
2584 ttyDisplay->cury = y;
2588 tty_putsym(window, x, y, ch)
2593 register struct WinDesc *cw = 0;
2595 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2596 panic(winpanicstr, window);
2598 print_vt_code2(AVTC_SELECT_WINDOW, window);
2604 tty_curs(window, x, y);
2608 if(cw->type!=NHW_MAP)
2609 (void) jputchar(ch);
2611 (void) cputchar(ch);
2620 impossible("Can't putsym to window type %d", cw->type);
2625 STATIC_OVL const char *
2629 static char cbuf[BUFSZ];
2631 /* compress out consecutive spaces if line is too long;
2632 topline wrapping converts space at wrap point into newline,
2633 we reverse that here */
2634 if ((int) strlen(str) >= CO || index(str, '\n')) {
2635 const char *in_str = str;
2636 char c, *outstr = cbuf, *outend = &cbuf[sizeof cbuf - 1];
2637 boolean was_space = TRUE; /* True discards all leading spaces;
2638 False would retain one if present */
2640 while ((c = *in_str++) != '\0' && outstr < outend) {
2643 if (was_space && c == ' ')
2646 was_space = (c == ' ');
2648 if ((was_space && outstr > cbuf) || outstr == outend)
2649 --outstr; /* remove trailing space or make room for terminator */
2657 tty_putstr(window, attr, str)
2662 register struct WinDesc *cw = 0;
2664 register const char *nb;
2665 register long i, j, n0;
2667 int kchar2 = 0; /* if 1, kanji 2nd byte */
2670 /* Assume there's a real problem if the window is missing --
2671 * probably a panic message
2674 jputchar('\0'); /* RESET */
2677 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) {
2682 if (str == (const char *) 0
2683 || ((cw->flags & WIN_CANCELLED) && (cw->type != NHW_MESSAGE)))
2685 if (cw->type != NHW_MESSAGE)
2686 str = compress_str(str);
2688 ttyDisplay->lastwin = window;
2690 print_vt_code2(AVTC_SELECT_WINDOW, window);
2694 /* really do this later */
2695 #if defined(USER_SOUNDS) && defined(WIN32CON)
2696 play_sound_for_message(str);
2702 ob = &cw->data[cw->cury][j = cw->curx];
2705 if (!cw->cury && (int) strlen(str) >= CO) {
2706 /* the characters before "St:" are unnecessary */
2707 nb = index(str, ':');
2708 if (nb && nb > str + 2)
2712 for (i = cw->curx + 1, n0 = cw->cols; i < n0; i++, nb++) {
2714 #ifndef STATUS_HILITES
2715 if (*ob || context.botlx) {
2717 /* STATUS_HILITES will call cl_end() when finished
2718 * its sequence of putstr's. We don't want to call
2719 * cl_end() with each putstr() which may cause flashing
2720 * in the Windows port
2722 if (context.botlx) {
2724 /* last char printed may be in middle of line */
2725 tty_curs(WIN_STATUS, i, cw->cury);
2731 #else /* this code updates all status line at any time */
2733 /* check 2-bytes character for Japanese */
2734 /* by issei 93/12/2 */
2735 uc = *((unsigned char *)nb);
2736 if((!(uc & 0x80) && *ob != *nb) || kflg){
2737 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2740 if(*ob != *nb || *(ob+1)!= *(nb+1)){
2741 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2746 #define ismbchar(c) (((unsigned char)(c)) & 0x80)
2749 if (kchar2) /* kanji 2nd byte */
2751 if (kchar2 & KUPDATE)
2752 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2755 else if (ismbchar(*nb)) /* kanji 1st byte */
2758 /* Kanji char must be checked as 2-bytes pair. */
2759 /* check i to prevent putting only kanji 1st byte at last. */
2760 if ((*nb != *ob || *(nb+1) != *(ob+1)) && i < n0-1)
2762 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2763 kchar2 |= KUPDATE; /* must do update */
2765 /* else nb is the same char as old, so need not to update */
2767 /* not kanji char */
2770 #ifdef STATUS_HILITES
2771 /* Don't optimize the putsym away, in case it happens
2772 to be the same character but different color/attr.
2773 We don't optimize on iflags.use_status_hilites either,
2774 in case old chars were written with highlighting and
2775 that option has just now been toggled off. [We could
2776 do better by tracking color/attr more closely.] */
2780 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2785 (void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1);
2786 cw->data[cw->cury][cw->cols - 1] = '\0'; /* null terminate */
2787 #ifndef STATUS_HILITES
2788 cw->cury = (cw->cury + 1) % 2;
2793 tty_curs(window, cw->curx + 1, cw->cury);
2794 term_start_attr(attr);
2795 while (*str && (int) ttyDisplay->curx < (int) ttyDisplay->cols - 1) {
2797 (void) putchar(*str);
2799 (void) cputchar(*str);
2806 term_end_attr(attr);
2809 tty_curs(window, cw->curx + 1, cw->cury);
2810 term_start_attr(attr);
2812 if ((int) ttyDisplay->curx >= (int) ttyDisplay->cols - 1) {
2815 tty_curs(window, cw->curx + 1, cw->cury);
2818 (void) putchar(*str);
2820 (void) jputchar(*str);
2827 term_end_attr(attr);
2832 if (cw->type == NHW_TEXT
2833 && (cw->cury + cw->offy) == ttyDisplay->rows - 1)
2835 if (cw->type == NHW_TEXT && cw->cury == ttyDisplay->rows - 1)
2838 /* not a menu, so save memory and output 1 page at a time */
2839 cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
2840 tty_display_nhwindow(window, TRUE);
2841 for (i = 0; i < cw->maxrow; i++)
2843 free((genericptr_t) cw->data[i]);
2846 cw->maxrow = cw->cury = 0;
2848 /* always grows one at a time, but alloc 12 at a time */
2849 if (cw->cury >= cw->rows) {
2853 tmp = (char **) alloc(sizeof(char *) * (unsigned) cw->rows);
2854 for (i = 0; i < cw->maxrow; i++)
2855 tmp[i] = cw->data[i];
2857 free((genericptr_t) cw->data);
2860 for (i = cw->maxrow; i < cw->rows; i++)
2863 if (cw->data[cw->cury])
2864 free((genericptr_t) cw->data[cw->cury]);
2865 n0 = (long) strlen(str) + 1L;
2866 ob = cw->data[cw->cury] = (char *) alloc((unsigned) n0 + 1);
2867 *ob++ = (char) (attr + 1); /* avoid nuls, for convenience */
2870 if (n0 > cw->maxcol)
2872 if (++cw->cury > cw->maxrow)
2873 cw->maxrow = cw->cury;
2875 /* attempt to break the line */
2876 for (i = CO - 1; i && str[i] != ' ' && str[i] != '\n';)
2879 cw->data[cw->cury - 1][++i] = '\0';
2880 tty_putstr(window, attr, &str[i]);
2886 jputchar('\0'); /* RESET */
2891 tty_display_file(fname, complain)
2895 #ifdef DEF_PAGER /* this implies that UNIX is defined */
2897 /* use external pager; this may give security problems */
2898 register int fd = open(fname, 0);
2902 pline("Cannot open %s.", fname);
2908 /* Now that child() does a setuid(getuid()) and a chdir(),
2909 we may not be able to open file fname anymore, so make
2914 raw_printf("Cannot open %s as stdin.", fname);
2916 (void) execlp(catmore, "page", (char *) 0);
2918 raw_printf("Cannot exec %s.", catmore);
2921 sleep(10); /* want to wait_synch() but stdin is gone */
2922 nh_terminate(EXIT_FAILURE);
2929 #else /* DEF_PAGER */
2935 tty_clear_nhwindow(WIN_MESSAGE);
2936 f = dlb_fopen(fname, "r");
2944 pline("Cannot open \"%s\".", fname);
2948 winid datawin = tty_create_nhwindow(NHW_TEXT);
2949 boolean empty = TRUE;
2956 /* attempt to scroll text below map window if there's room */
2957 wins[datawin]->offy = wins[WIN_STATUS]->offy + 3;
2958 if ((int) wins[datawin]->offy + 12 > (int) ttyDisplay->rows)
2959 wins[datawin]->offy = 0;
2961 while (dlb_fgets(buf, BUFSZ, f)) {
2962 if ((cr = index(buf, '\n')) != 0)
2965 if ((cr = index(buf, '\r')) != 0)
2968 if (index(buf, '\t') != 0)
2969 (void) tabexpand(buf);
2971 tty_putstr(datawin, 0, buf);
2972 if (wins[datawin]->flags & WIN_CANCELLED)
2976 tty_display_nhwindow(datawin, FALSE);
2977 tty_destroy_nhwindow(datawin);
2978 (void) dlb_fclose(f);
2981 #endif /* DEF_PAGER */
2985 tty_start_menu(window)
2988 tty_clear_nhwindow(window);
2994 * Add a menu item to the beginning of the menu list. This list is reversed
2998 tty_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
2999 winid window; /* window to use, must be of type NHW_MENU */
3000 int glyph UNUSED; /* glyph to display with item (not used) */
3001 const anything *identifier; /* what to return if selected */
3002 char ch; /* keyboard accelerator (0 = pick our own) */
3003 char gch; /* group accelerator (0 = no group) */
3004 int attr; /* attribute for string (like tty_putstr()) */
3005 const char *str; /* menu string */
3006 boolean preselected; /* item is marked as selected */
3008 register struct WinDesc *cw = 0;
3009 tty_menu_item *item;
3011 char buf[4 + BUFSZ];
3013 if (str == (const char *) 0)
3016 if (window == WIN_ERR
3017 || (cw = wins[window]) == (struct WinDesc *) 0
3018 || cw->type != NHW_MENU)
3019 panic(winpanicstr, window);
3022 if (identifier->a_void) {
3023 int len = strlen(str);
3026 /* We *think* everything's coming in off at most BUFSZ bufs... */
3027 impossible("Menu item too long (%d).", len);
3030 Sprintf(buf, "%c - ", ch ? ch : '?');
3031 (void) strncpy(buf + 4, str, len);
3032 buf[4 + len] = '\0';
3037 item = (tty_menu_item *) alloc(sizeof(tty_menu_item));
3038 item->identifier = *identifier;
3040 item->selected = preselected;
3041 item->selector = ch;
3042 item->gselector = gch;
3044 item->str = dupstr(newstr ? newstr : "");
3046 item->next = cw->mlist;
3050 /* Invert the given list, can handle NULL as an input. */
3051 STATIC_OVL tty_menu_item *
3053 tty_menu_item *curr;
3055 tty_menu_item *next, *head = 0;
3067 * End a menu in this window, window must a type NHW_MENU. This routine
3068 * processes the string list. We calculate the # of pages, then assign
3069 * keyboard accelerators as needed. Finally we decide on the width and
3070 * height of the window.
3073 tty_end_menu(window, prompt)
3074 winid window; /* menu to use */
3075 const char *prompt; /* prompt to for menu */
3077 struct WinDesc *cw = 0;
3078 tty_menu_item *curr;
3083 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
3084 || cw->type != NHW_MENU)
3085 panic(winpanicstr, window);
3087 /* Reverse the list so that items are in correct order. */
3088 cw->mlist = reverse(cw->mlist);
3090 /* Put the prompt at the beginning of the menu. */
3094 any = zeroany; /* not selectable */
3095 tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
3097 tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt,
3101 /* XXX another magic number? 52 */
3102 lmax = min(52, (int) ttyDisplay->rows - 1); /* # lines per page */
3103 cw->npages = (cw->nitems + (lmax - 1)) / lmax; /* # of pages */
3105 /* make sure page list is large enough */
3106 if (cw->plist_size < cw->npages + 1 /*need 1 slot beyond last*/) {
3108 free((genericptr_t) cw->plist);
3109 cw->plist_size = cw->npages + 1;
3110 cw->plist = (tty_menu_item **) alloc(cw->plist_size
3111 * sizeof(tty_menu_item *));
3114 cw->cols = 0; /* cols is set when the win is initialized... (why?) */
3115 menu_ch = '?'; /* lint suppression */
3116 for (n = 0, curr = cw->mlist; curr; n++, curr = curr->next) {
3117 /* set page boundaries and character accelerators */
3118 if ((n % lmax) == 0) {
3120 cw->plist[n / lmax] = curr;
3122 if (curr->identifier.a_void && !curr->selector) {
3123 curr->str[0] = curr->selector = menu_ch;
3124 if (menu_ch++ == 'z')
3128 /* cut off any lines that are too long */
3129 len = strlen(curr->str) + 2; /* extra space at beg & end */
3130 if (len > (int) ttyDisplay->cols) {
3131 curr->str[ttyDisplay->cols - 2] = 0;
3132 len = ttyDisplay->cols;
3137 cw->plist[cw->npages] = 0; /* plist terminator */
3140 * If greater than 1 page, morestr is "(x of y) " otherwise, "(end) "
3142 if (cw->npages > 1) {
3144 /* produce the largest demo string */
3145 Sprintf(buf, "(%ld of %ld) ", cw->npages, cw->npages);
3147 cw->morestr = dupstr("");
3149 cw->morestr = dupstr("(end) ");
3150 len = strlen(cw->morestr);
3153 if (len > (int) ttyDisplay->cols) {
3154 /* truncate the prompt if it's too long for the screen */
3155 if (cw->npages <= 1) /* only str in single page case */
3156 cw->morestr[ttyDisplay->cols] = 0;
3157 len = ttyDisplay->cols;
3162 cw->maxcol = cw->cols;
3165 * The number of lines in the first page plus the morestr will be the
3166 * maximum size of the window.
3169 cw->maxrow = cw->rows = lmax + 1;
3171 cw->maxrow = cw->rows = cw->nitems + 1;
3175 tty_select_menu(window, how, menu_list)
3178 menu_item **menu_list;
3180 register struct WinDesc *cw = 0;
3181 tty_menu_item *curr;
3185 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
3186 || cw->type != NHW_MENU)
3187 panic(winpanicstr, window);
3189 *menu_list = (menu_item *) 0;
3190 cw->how = (short) how;
3192 tty_display_nhwindow(window, TRUE);
3193 cancelled = !!(cw->flags & WIN_CANCELLED);
3194 tty_dismiss_nhwindow(window); /* does not destroy window data */
3199 for (n = 0, curr = cw->mlist; curr; curr = curr->next)
3205 *menu_list = (menu_item *) alloc(n * sizeof(menu_item));
3206 for (mi = *menu_list, curr = cw->mlist; curr; curr = curr->next)
3207 if (curr->selected) {
3208 mi->item = curr->identifier;
3209 mi->count = curr->count;
3217 /* special hack for treating top line --More-- as a one item menu */
3219 tty_message_menu(let, how, mesg)
3224 /* "menu" without selection; use ordinary pline, no more() */
3225 if (how == PICK_NONE) {
3230 ttyDisplay->dismiss_more = let;
3232 /* barebones pline(); since we're only supposed to be called after
3233 response to a prompt, we'll assume that the display is up to date */
3234 tty_putstr(WIN_MESSAGE, 0, mesg);
3235 /* if `mesg' didn't wrap (triggering --More--), force --More-- now */
3236 if (ttyDisplay->toplin == 1) {
3238 ttyDisplay->toplin = 1; /* more resets this */
3239 tty_clear_nhwindow(WIN_MESSAGE);
3241 /* normally <ESC> means skip further messages, but in this case
3242 it means cancel the current prompt; any other messages should
3243 continue to be output normally */
3244 wins[WIN_MESSAGE]->flags &= ~WIN_CANCELLED;
3245 ttyDisplay->dismiss_more = 0;
3247 return ((how == PICK_ONE && morc == let) || morc == '\033') ? morc : '\0';
3251 tty_update_inventory()
3259 (void) fflush(stdout);
3265 /* we just need to make sure all windows are synch'd */
3266 if (!ttyDisplay || ttyDisplay->rawprint) {
3269 ttyDisplay->rawprint = 0;
3271 tty_display_nhwindow(WIN_MAP, FALSE);
3272 if (ttyDisplay->inmore) {
3273 addtopl("--More--");
3274 (void) fflush(stdout);
3275 } else if (ttyDisplay->inread > program_state.gameover) {
3276 /* this can only happen if we were reading and got interrupted */
3277 ttyDisplay->toplin = 3;
3278 /* do this twice; 1st time gets the Quit? message again */
3279 (void) tty_doprev_message();
3280 (void) tty_doprev_message();
3282 (void) fflush(stdout);
3288 docorner(xmin, ymax)
3289 register int xmin, ymax;
3292 register struct WinDesc *cw = wins[WIN_MAP];
3294 #if 0 /* this optimization is not valuable enough to justify
3295 abusing core internals... */
3296 if (u.uswallow) { /* Can be done more efficiently */
3298 /* without this flush, if we happen to follow --More-- displayed in
3299 leftmost column, the cursor gets left in the wrong place after
3300 <docorner<more<update_topl<tty_putstr calls unwind back to core */
3306 #if defined(SIGWINCH) && defined(CLIPPING)
3308 ymax = LI; /* can happen if window gets smaller */
3310 for (y = 0; y < ymax; y++) {
3311 tty_curs(BASE_WINDOW, xmin, y); /* move cursor */
3312 cl_end(); /* clear to end of line */
3314 if (y < (int) cw->offy || y + clipy > ROWNO)
3315 continue; /* only refresh board */
3316 #if defined(USE_TILES) && defined(MSDOS)
3317 if (iflags.tile_view)
3318 row_refresh((xmin / 2) + clipx - ((int) cw->offx / 2), COLNO - 1,
3319 y + clipy - (int) cw->offy);
3322 row_refresh(xmin + clipx - (int) cw->offx, COLNO - 1,
3323 y + clipy - (int) cw->offy);
3325 if (y < cw->offy || y > ROWNO)
3326 continue; /* only refresh board */
3327 row_refresh(xmin - (int) cw->offx, COLNO - 1, y - (int) cw->offy);
3332 if (ymax >= (int) wins[WIN_STATUS]->offy) {
3333 /* we have wrecked the bottom line */
3342 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3349 if (ttyDisplay->color != NO_COLOR) {
3351 ttyDisplay->color = NO_COLOR;
3361 register char ch = (char) in_ch;
3363 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3364 if (SYMHANDLING(H_IBM) || iflags.eight_bit_tty) {
3365 /* IBM-compatible displays don't need other stuff */
3369 (void) cputchar(ch);
3371 } else if (ch & 0x80) {
3372 if (!GFlag || HE_resets_AS) {
3377 (void) putchar((ch ^ 0x80)); /* Strip 8th bit */
3379 (void) cputchar((ch ^ 0x80)); /* Strip 8th bit */
3389 (void) jputchar(ch);
3396 #endif /* ASCIIGRAPH && !NO_TERMS */
3413 tty_cliparound(x, y)
3416 extern boolean restoring;
3417 int oldx = clipx, oldy = clipy;
3421 if (x < clipx + 5) {
3422 clipx = max(0, x - 20);
3423 clipxmax = clipx + CO;
3424 } else if (x > clipxmax - 5) {
3425 clipxmax = min(COLNO, clipxmax + 20);
3426 clipx = clipxmax - CO;
3428 if (y < clipy + 2) {
3429 clipy = max(0, y - (clipymax - clipy) / 2);
3430 clipymax = clipy + (LI - 3);
3431 } else if (y > clipymax - 2) {
3432 clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2);
3433 clipy = clipymax - (LI - 3);
3435 if (clipx != oldx || clipy != oldy) {
3436 if (on_level(&u.uz0, &u.uz) && !restoring)
3440 #endif /* CLIPPING */
3445 * Print the glyph to the output device. Don't flush the output device.
3447 * Since this is only called from show_glyph(), it is assumed that the
3448 * position and glyph are always correct (checked there)!
3452 tty_print_glyph(window, x, y, glyph, bkglyph)
3459 boolean reverse_on = FALSE;
3465 if (x <= clipx || y < clipy || x >= clipxmax || y >= clipymax)
3469 /* map glyph to character and color */
3470 (void) mapglyph(glyph, &ch, &color, &special, x, y);
3472 print_vt_code2(AVTC_SELECT_WINDOW, window);
3474 /* Move the cursor. */
3475 tty_curs(window, x, y);
3477 print_vt_code3(AVTC_GLYPH_START, glyph2tile[glyph], special);
3480 if (ul_hack && ch == '_') { /* non-destructive underscore */
3482 (void) putchar((char) ' ');
3484 (void) cputchar((char) ' ');
3491 if (color != ttyDisplay->color) {
3492 if (ttyDisplay->color != NO_COLOR)
3494 ttyDisplay->color = color;
3495 if (color != NO_COLOR)
3496 term_start_color(color);
3498 #endif /* TEXTCOLOR */
3500 /* must be after color check; term_end_color may turn off inverse too */
3501 if (((special & MG_PET) && iflags.hilite_pet)
3502 || ((special & MG_OBJPILE) && iflags.hilite_pile)
3503 || ((special & MG_DETECT) && iflags.use_inverse)
3504 || ((special & MG_BW_LAVA) && iflags.use_inverse)) {
3505 term_start_attr(ATR_INVERSE);
3509 #if defined(USE_TILES) && defined(MSDOS)
3510 if (iflags.grmode && iflags.tile_view)
3511 xputg(glyph, ch, special);
3514 g_putch(ch); /* print the character */
3517 term_end_attr(ATR_INVERSE);
3519 /* turn off color as well, ATR_INVERSE may have done this already */
3520 if (ttyDisplay->color != NO_COLOR) {
3522 ttyDisplay->color = NO_COLOR;
3527 print_vt_code1(AVTC_GLYPH_END);
3529 wins[window]->curx++; /* one character over */
3530 ttyDisplay->curx++; /* the real cursor moved too */
3538 ttyDisplay->rawprint++;
3539 print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
3540 #if defined(MICRO) || defined(WIN32CON)
3548 (void) fflush(stdout);
3553 tty_raw_print_bold(str)
3557 ttyDisplay->rawprint++;
3558 print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
3559 term_start_raw_bold();
3560 #if defined(MICRO) || defined(WIN32CON)
3564 (void) fputs(str, stdout);
3569 term_end_raw_bold();
3570 #if defined(MICRO) || defined(WIN32CON)
3576 (void) fflush(stdout);
3585 /* kludge alert: Some Unix variants return funny values if getc()
3586 * is called, interrupted, and then called again. There
3587 * is non-reentrant code in the internal _filbuf() routine, called by
3590 static volatile int nesting = 0;
3594 print_vt_code1(AVTC_INLINE_SYNC);
3595 (void) fflush(stdout);
3596 /* Note: if raw_print() and wait_synch() get called to report terminal
3597 * initialization problems, then wins[] and ttyDisplay might not be
3598 * available yet. Such problems will probably be fatal before we get
3599 * here, but validate those pointers just in case...
3601 if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
3602 wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
3604 i = (++nesting == 1) ? tgetch()
3605 : (read(fileno(stdin), (genericptr_t) &nestbuf, 1)
3606 == 1) ? (int) nestbuf : EOF;
3612 i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
3614 i = '\033'; /* same for EOF */
3615 if (ttyDisplay && ttyDisplay->toplin == 1)
3616 ttyDisplay->toplin = 2;
3617 #ifdef TTY_TILES_ESCCODES
3619 /* hack to force output of the window select code */
3620 int tmp = vt_tile_current_window;
3621 vt_tile_current_window++;
3622 print_vt_code2(AVTC_SELECT_WINDOW, tmp);
3624 #endif /* TTY_TILES_ESCCODES */
3629 * return a key, or 0, in which case a mouse button was pressed
3630 * mouse events should be returned as character postitions in the map window.
3631 * Since normal tty's don't have mice, just return a key.
3635 tty_nh_poskey(x, y, mod)
3638 #if defined(WIN32CON)
3640 (void) fflush(stdout);
3641 /* Note: if raw_print() and wait_synch() get called to report terminal
3642 * initialization problems, then wins[] and ttyDisplay might not be
3643 * available yet. Such problems will probably be fatal before we get
3644 * here, but validate those pointers just in case...
3646 if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
3647 wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
3648 i = ntposkey(x, y, mod);
3649 if (!i && mod && (*mod == 0 || *mod == EOF))
3650 i = '\033'; /* map NUL or EOF to ESC, nethack doesn't expect either */
3651 if (ttyDisplay && ttyDisplay->toplin == 1)
3652 ttyDisplay->toplin = 2;
3654 #else /* !WIN32CON */
3659 return tty_nhgetch();
3660 #endif /* ?WIN32CON */
3669 #if defined(WIN32CON)
3670 if (!strncmpi(windowprocs.name, "tty", 3))
3678 tty_update_positionbar(posbar)
3682 video_update_positionbar(posbar);
3688 * The following data structures come from the genl_ routines in
3689 * src/windows.c and as such are considered to be on the window-port
3690 * "side" of things, rather than the NetHack-core "side" of things.
3693 extern const char *status_fieldfmt[MAXBLSTATS];
3694 extern char *status_vals[MAXBLSTATS];
3695 extern boolean status_activefields[MAXBLSTATS];
3696 extern winid WIN_STATUS;
3698 #ifdef STATUS_HILITES
3699 static long tty_condition_bits;
3700 static int tty_status_colors[MAXBLSTATS];
3701 int hpbar_percent, hpbar_color;
3703 static int FDECL(condcolor, (long, unsigned long *));
3704 static int FDECL(condattr, (long, unsigned long *));
3705 #endif /* STATUS_HILITES */
3710 #ifdef STATUS_HILITES
3713 for (i = 0; i < MAXBLSTATS; ++i)
3714 tty_status_colors[i] = NO_COLOR; /* no color */
3715 tty_condition_bits = 0L;
3716 hpbar_percent = 0, hpbar_color = NO_COLOR;
3717 #endif /* STATUS_HILITES */
3719 /* let genl_status_init do most of the initialization */
3725 * -- update the value of a status field.
3726 * -- the fldindex identifies which field is changing and
3727 * is an integer index value from botl.h
3728 * -- fldindex could be any one of the following from botl.h:
3729 * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3730 * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
3731 * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
3732 * BL_LEVELDESC, BL_EXP, BL_CONDITION
3733 * -- fldindex could also be BL_FLUSH (-1), which is not really
3734 * a field index, but is a special trigger to tell the
3735 * windowport that it should redisplay all its status fields,
3736 * even if no changes have been presented to it.
3737 * -- ptr is usually a "char *", unless fldindex is BL_CONDITION.
3738 * If fldindex is BL_CONDITION, then ptr is a long value with
3739 * any or none of the following bits set (from botl.h):
3740 * BL_MASK_STONE 0x00000001L
3741 * BL_MASK_SLIME 0x00000002L
3742 * BL_MASK_STRNGL 0x00000004L
3743 * BL_MASK_FOODPOIS 0x00000008L
3744 * BL_MASK_TERMILL 0x00000010L
3745 * BL_MASK_BLIND 0x00000020L
3746 * BL_MASK_DEAF 0x00000040L
3747 * BL_MASK_STUN 0x00000080L
3748 * BL_MASK_CONF 0x00000100L
3749 * BL_MASK_HALLU 0x00000200L
3750 * BL_MASK_LEV 0x00000400L
3751 * BL_MASK_FLY 0x00000800L
3752 * BL_MASK_RIDE 0x00001000L
3753 * -- The value passed for BL_GOLD includes an encoded leading
3754 * symbol for GOLD "\GXXXXNNNN:nnn". If the window port needs to use
3755 * the textual gold amount without the leading "$:" the port will
3756 * have to skip past ':' in the passed "ptr" for the BL_GOLD case.
3757 * -- color is an unsigned int.
3758 * color_index = color & 0x00FF; CLR_* value
3759 * attribute = color & 0xFF00 >> 8; BL_* values
3760 * This holds the color and attribute that the field should
3762 * This is relevant for everything except BL_CONDITION fldindex.
3763 * If fldindex is BL_CONDITION, this parameter should be ignored,
3764 * as condition hilighting is done via the next colormasks
3765 * parameter instead.
3767 * -- colormasks - pointer to cond_hilites[] array of colormasks.
3768 * Only relevant for BL_CONDITION fldindex. The window port
3769 * should ignore this parameter for other fldindex values.
3770 * Each condition bit must only ever appear in one of the
3771 * CLR_ array members, but can appear in multiple HL_ATTCLR_
3772 * offsets (because more than one attribute can co-exist).
3773 * See doc/window.doc for more details.
3777 /* new approach through status_update() only */
3778 #define Begin_Attr(m) \
3780 if ((m) & HL_BOLD) \
3781 term_start_attr(ATR_BOLD); \
3782 if ((m) & HL_INVERSE) \
3783 term_start_attr(ATR_INVERSE); \
3784 if ((m) & HL_ULINE) \
3785 term_start_attr(ATR_ULINE); \
3786 if ((m) & HL_BLINK) \
3787 term_start_attr(ATR_BLINK); \
3789 term_start_attr(ATR_DIM); \
3792 #define End_Attr(m) \
3795 term_end_attr(ATR_DIM); \
3796 if ((m) & HL_BLINK) \
3797 term_end_attr(ATR_BLINK); \
3798 if ((m) & HL_ULINE) \
3799 term_end_attr(ATR_ULINE); \
3800 if ((m) & HL_INVERSE) \
3801 term_end_attr(ATR_INVERSE); \
3802 if ((m) & HL_BOLD) \
3803 term_end_attr(ATR_BOLD); \
3806 #ifdef STATUS_HILITES
3809 #define MaybeDisplayCond(bm,txt) \
3810 if (tty_condition_bits & (bm)) { \
3811 putstr(WIN_STATUS, 0, " "); \
3812 if (iflags.hilite_delta) { \
3813 attrmask = condattr(bm, colormasks); \
3814 Begin_Attr(attrmask); \
3815 if ((coloridx = condcolor(bm, colormasks)) != NO_COLOR) \
3816 term_start_color(coloridx); \
3818 putstr(WIN_STATUS, 0, txt); \
3819 if (iflags.hilite_delta) { \
3820 if (coloridx != NO_COLOR) \
3822 End_Attr(attrmask); \
3826 #define MaybeDisplayCond(bm,txt) \
3827 if (tty_condition_bits & (bm)) { \
3828 putstr(WIN_STATUS, 0, " "); \
3829 if (iflags.hilite_delta) { \
3830 attrmask = condattr(bm, colormasks); \
3831 Begin_Attr(attrmask); \
3833 putstr(WIN_STATUS, 0, txt); \
3834 if (iflags.hilite_delta) { \
3835 End_Attr(attrmask); \
3841 tty_status_update(fldidx, ptr, chg, percent, color, colormasks)
3842 int fldidx, chg UNUSED, percent UNUSED, color;
3844 unsigned long *colormasks;
3846 long *condptr = (long *) ptr;
3847 int i, attrmask = 0;
3849 int coloridx = NO_COLOR;
3851 char *text = (char *) ptr;
3852 static boolean oncearound = FALSE; /* prevent premature partial display */
3853 enum statusfields fieldorder[2][15] = {
3854 { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN,
3855 BL_SCORE, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH,
3857 { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
3858 BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER,
3859 BL_CAP, BL_CONDITION, BL_FLUSH }
3863 if (fldidx != BL_FLUSH) {
3864 if (!status_activefields[fldidx])
3868 tty_condition_bits = *condptr;
3872 Sprintf(status_vals[fldidx],
3873 (fldidx == BL_TITLE && iflags.wc2_hitpointbar) ? "%-30s" :
3874 status_fieldfmt[fldidx] ? status_fieldfmt[fldidx] : "%s",
3877 tty_status_colors[fldidx] = color;
3879 tty_status_colors[fldidx] = NO_COLOR;
3881 if (iflags.wc2_hitpointbar && fldidx == BL_HP) {
3882 hpbar_percent = percent;
3884 hpbar_color = color;
3886 hpbar_color = NO_COLOR;
3893 if (!oncearound) return;
3895 curs(WIN_STATUS, 1, 0);
3896 for (i = 0; fieldorder[0][i] != BL_FLUSH; ++i) {
3897 int fldidx1 = fieldorder[0][i];
3898 if (status_activefields[fldidx1]) {
3899 if (fldidx1 != BL_TITLE || !iflags.wc2_hitpointbar) {
3901 coloridx = tty_status_colors[fldidx1] & 0x00FF;
3903 attridx = (tty_status_colors[fldidx1] & 0xFF00) >> 8;
3904 text = status_vals[fldidx1];
3905 if (iflags.hilite_delta) {
3907 putstr(WIN_STATUS, 0, " ");
3910 /* multiple attributes can be in effect concurrently */
3911 Begin_Attr(attridx);
3913 if (coloridx != NO_COLOR && coloridx != CLR_MAX)
3914 term_start_color(coloridx);
3918 putstr(WIN_STATUS, 0, text);
3920 if (iflags.hilite_delta) {
3922 if (coloridx != NO_COLOR)
3928 /* hitpointbar using hp percent calculation */
3929 int bar_pos, bar_len;
3930 char *bar2 = (char *)0;
3931 char bar[MAXCO], savedch;
3932 boolean twoparts = FALSE;
3934 text = status_vals[fldidx1];
3935 bar_len = strlen(text);
3936 if (bar_len < MAXCO-1) {
3938 bar_pos = (bar_len * hpbar_percent) / 100;
3939 if (bar_pos < 1 && hpbar_percent > 0)
3941 if (bar_pos >= bar_len && hpbar_percent < 100)
3942 bar_pos = bar_len - 1;
3943 if (bar_pos > 0 && bar_pos < bar_len) {
3945 bar2 = &bar[bar_pos];
3950 if (iflags.hilite_delta && iflags.wc2_hitpointbar) {
3951 putstr(WIN_STATUS, 0, "[");
3953 coloridx = hpbar_color & 0x00FF;
3954 /* attridx = (hpbar_color & 0xFF00) >> 8; */
3955 if (coloridx != NO_COLOR)
3956 term_start_color(coloridx);
3958 term_start_attr(ATR_INVERSE);
3959 putstr(WIN_STATUS, 0, bar);
3960 term_end_attr(ATR_INVERSE);
3962 if (coloridx != NO_COLOR)
3967 putstr(WIN_STATUS, 0, bar2);
3969 putstr(WIN_STATUS, 0, "]");
3971 putstr(WIN_STATUS, 0, text);
3976 curs(WIN_STATUS, 1, 1);
3977 for (i = 0; fieldorder[1][i] != BL_FLUSH; ++i) {
3978 int fldidx2 = fieldorder[1][i];
3980 if (status_activefields[fldidx2]) {
3981 if (fldidx2 != BL_CONDITION) {
3983 coloridx = tty_status_colors[fldidx2] & 0x00FF;
3985 attridx = (tty_status_colors[fldidx2] & 0xFF00) >> 8;
3986 text = status_vals[fldidx2];
3987 if (iflags.hilite_delta) {
3989 putstr(WIN_STATUS, 0, " ");
3992 /* multiple attributes can be in effect concurrently */
3993 Begin_Attr(attridx);
3995 if (coloridx != NO_COLOR && coloridx != CLR_MAX)
3996 term_start_color(coloridx);
4000 if (fldidx2 == BL_GOLD) {
4001 /* putmixed() due to GOLD glyph */
4002 putmixed(WIN_STATUS, 0, text);
4004 putstr(WIN_STATUS, 0, text);
4007 if (iflags.hilite_delta) {
4009 if (coloridx != NO_COLOR)
4015 MaybeDisplayCond(BL_MASK_STONE, "Stone");
4016 MaybeDisplayCond(BL_MASK_SLIME, "Slime");
4017 MaybeDisplayCond(BL_MASK_STRNGL, "Strngl");
4018 MaybeDisplayCond(BL_MASK_FOODPOIS, "FoodPois");
4019 MaybeDisplayCond(BL_MASK_TERMILL, "TermIll");
4020 MaybeDisplayCond(BL_MASK_BLIND, "Blind");
4021 MaybeDisplayCond(BL_MASK_DEAF, "Deaf");
4022 MaybeDisplayCond(BL_MASK_STUN, "Stun");
4023 MaybeDisplayCond(BL_MASK_CONF, "Conf");
4024 MaybeDisplayCond(BL_MASK_HALLU, "Hallu");
4025 MaybeDisplayCond(BL_MASK_LEV, "Lev");
4026 MaybeDisplayCond(BL_MASK_FLY, "Fly");
4027 MaybeDisplayCond(BL_MASK_RIDE, "Ride");
4037 * Return what color this condition should
4038 * be displayed in based on user settings.
4040 int condcolor(bm, bmarray)
4042 unsigned long *bmarray;
4047 for (i = 0; i < CLR_MAX; ++i) {
4048 if (bmarray[i] && (bm & bmarray[i]))
4053 #endif /* TEXTCOLOR */
4055 int condattr(bm, bmarray)
4057 unsigned long *bmarray;
4062 if (bm && bmarray) {
4063 for (i = HL_ATTCLR_DIM; i < BL_ATTCLR_MAX; ++i) {
4064 if (bmarray[i] && (bm & bmarray[i])) {
4069 case HL_ATTCLR_BLINK:
4072 case HL_ATTCLR_ULINE:
4075 case HL_ATTCLR_INVERSE:
4078 case HL_ATTCLR_BOLD:
4087 #endif /* STATUS_HILITES */
4089 #endif /* TTY_GRAPHICS */