1 /* NetHack 3.6 wintty.c $NHDT-Date: 1575245194 2019/12/02 00:06:34 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.227 $ */
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-2020 */
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 #ifdef HANGUP_HANDLING
55 * NetHack's core switches to a dummy windowing interface when it
56 * detects SIGHUP, but that's no help for any interface routine which
57 * is already in progress at the time, and there have been reports of
58 * runaway disconnected processes which use up all available CPU time.
59 * HUPSKIP() and HUPSKIP_RETURN(x) are used to try to cut them off so
60 * that they return to the core instead attempting more terminal I/O.
64 if (program_state.done_hup) { \
69 /* morc=ESC - in case we bypass xwaitforspace() which sets that */
70 #define HUPSKIP_RESULT(RES) \
72 if (program_state.done_hup) \
75 #else /* !HANGUP_HANDLING */
76 #define HUPSKIP() /*empty*/
77 #define HUPSKIP_RESULT(RES) /*empty*/
78 #endif /* ?HANGUP_HANDLING */
80 extern char mapped_menu_cmds[]; /* from options.c */
82 /* Interface definition, for windows.c */
83 struct window_procs tty_procs = {
87 | WC_TILED_MAP | WC_ASCII_MAP
92 | WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN),
94 #if defined(SELECTSAVED)
97 #if defined(STATUS_HILITES)
98 | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS
101 | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_STATUSLINES),
103 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */
105 {1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1},
107 tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event,
108 tty_exit_nhwindows, tty_suspend_nhwindows, tty_resume_nhwindows,
109 tty_create_nhwindow, tty_clear_nhwindow, tty_display_nhwindow,
110 tty_destroy_nhwindow, tty_curs, tty_putstr, genl_putmixed,
111 tty_display_file, tty_start_menu, tty_add_menu, tty_end_menu,
112 tty_select_menu, tty_message_menu, tty_update_inventory, tty_mark_synch,
118 tty_update_positionbar,
120 tty_print_glyph, tty_raw_print, tty_raw_print_bold, tty_nhgetch,
121 tty_nh_poskey, tty_nhbell, tty_doprev_message, tty_yn_function,
122 tty_getlin, tty_get_ext_cmd, tty_number_pad, tty_delay_output,
123 #ifdef CHANGE_COLOR /* the Mac uses a palette device */
126 tty_change_background, set_tty_font_name,
128 tty_get_color_string,
131 /* other defs that really should go away (they're tty specific) */
132 tty_start_screen, tty_end_screen, genl_outrip,
133 tty_preference_update,
134 tty_getmsghistory, tty_putmsghistory,
136 genl_status_finish, tty_status_enablefield,
137 #ifdef STATUS_HILITES
142 genl_can_suspend_yes,
146 struct WinDesc *wins[MAXWIN];
147 struct DisplayDesc *ttyDisplay; /* the tty display descriptor */
149 extern void FDECL(cmov, (int, int)); /* from termcap.c */
150 extern void FDECL(nocmov, (int, int)); /* from termcap.c */
151 #if defined(UNIX) || defined(VMS)
152 static char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */
155 static char winpanicstr[] = "Bad window id %d";
156 char defmorestr[] = "--More--";
159 #if defined(USE_TILES) && defined(MSDOS)
160 boolean clipping = FALSE; /* clipping on? */
161 int clipx = 0, clipxmax = 0;
163 static boolean clipping = FALSE; /* clipping on? */
164 static int clipx = 0, clipxmax = 0;
166 static int clipy = 0, clipymax = 0;
167 #endif /* CLIPPING */
169 #if defined(USE_TILES) && defined(MSDOS)
170 extern void FDECL(adjust_cursor_flags, (struct WinDesc *));
173 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
174 boolean GFlag = FALSE;
175 boolean HE_resets_AS; /* see termcap.c */
178 #if defined(MICRO) || defined(WIN32CON)
179 static const char to_continue[] = "to continue";
180 #define getret() getreturn(to_continue)
182 STATIC_DCL void NDECL(getret);
184 STATIC_DCL void FDECL(bail, (const char *)); /* __attribute__((noreturn)) */
185 STATIC_DCL void NDECL(new_status_window);
186 STATIC_DCL void FDECL(erase_menu_or_text, (winid, struct WinDesc *,
188 STATIC_DCL void FDECL(free_window_info, (struct WinDesc *, BOOLEAN_P));
189 STATIC_DCL void FDECL(dmore, (struct WinDesc *, const char *));
190 STATIC_DCL void FDECL(set_item_state, (winid, int, tty_menu_item *));
191 STATIC_DCL void FDECL(set_all_on_page, (winid, tty_menu_item *,
193 STATIC_DCL void FDECL(unset_all_on_page, (winid, tty_menu_item *,
195 STATIC_DCL void FDECL(invert_all_on_page, (winid, tty_menu_item *,
196 tty_menu_item *, CHAR_P));
197 STATIC_DCL void FDECL(invert_all, (winid, tty_menu_item *,
198 tty_menu_item *, CHAR_P));
199 STATIC_DCL void FDECL(toggle_menu_attr, (BOOLEAN_P, int, int));
200 STATIC_DCL void FDECL(process_menu_window, (winid, struct WinDesc *));
201 STATIC_DCL void FDECL(process_text_window, (winid, struct WinDesc *));
202 STATIC_DCL tty_menu_item *FDECL(reverse, (tty_menu_item *));
203 STATIC_DCL const char *FDECL(compress_str, (const char *));
204 STATIC_DCL void FDECL(tty_putsym, (winid, int, int, CHAR_P));
205 STATIC_DCL void FDECL(setup_rolemenu, (winid, BOOLEAN_P, int, int, int));
206 STATIC_DCL void FDECL(setup_racemenu, (winid, BOOLEAN_P, int, int, int));
207 STATIC_DCL void FDECL(setup_gendmenu, (winid, BOOLEAN_P, int, int, int));
208 STATIC_DCL void FDECL(setup_algnmenu, (winid, BOOLEAN_P, int, int, int));
209 STATIC_DCL boolean NDECL(reset_role_filtering);
210 #ifdef STATUS_HILITES
211 STATIC_DCL boolean FDECL(check_fields, (BOOLEAN_P, int *));
212 STATIC_DCL void NDECL(render_status);
213 STATIC_DCL void FDECL(tty_putstatusfield, (const char *, int, int));
214 STATIC_DCL boolean NDECL(check_windowdata);
215 STATIC_DCL void NDECL(set_condition_length);
216 STATIC_DCL int FDECL(make_things_fit, (BOOLEAN_P));
217 STATIC_DCL void FDECL(shrink_enc, (int));
218 STATIC_DCL void FDECL(shrink_dlvl, (int));
219 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
220 STATIC_DCL void NDECL(status_sanity_check);
221 #endif /* NH_DEVEL_STATUS */
225 * A string containing all the default commands -- to add to a list
226 * of acceptable inputs.
228 static const char default_menu_cmds[] = {
229 MENU_FIRST_PAGE, MENU_LAST_PAGE, MENU_NEXT_PAGE,
230 MENU_PREVIOUS_PAGE, MENU_SELECT_ALL, MENU_UNSELECT_ALL,
231 MENU_INVERT_ALL, MENU_SELECT_PAGE, MENU_UNSELECT_PAGE,
232 MENU_INVERT_PAGE, MENU_SEARCH, 0 /* null terminator */
235 #ifdef TTY_TILES_ESCCODES
236 static int vt_tile_current_window = -2;
239 print_vt_code(i, c, d)
243 if (iflags.vt_tiledata) {
245 if (i == AVTC_SELECT_WINDOW) {
246 if (c == vt_tile_current_window)
248 vt_tile_current_window = c;
251 printf("\033[1;%d;%d;%d%c", i, c, d, TILE_ANSI_COMMAND);
253 printf("\033[1;%d;%d%c", i, c, TILE_ANSI_COMMAND);
255 printf("\033[1;%d%c", i, TILE_ANSI_COMMAND);
260 # define print_vt_code(i, c, d) ;
261 #endif /* !TTY_TILES_ESCCODES */
262 #define print_vt_code1(i) print_vt_code((i), -1, -1)
263 #define print_vt_code2(i,c) print_vt_code((i), (c), -1)
264 #define print_vt_code3(i,c,d) print_vt_code((i), (c), (d))
267 /* clean up and quit */
273 tty_exit_nhwindows(mesg);
274 nh_terminate(EXIT_SUCCESS);
278 #if defined(SIGWINCH) && defined(CLIPPING)
279 STATIC_DCL void FDECL(winch_handler, (int));
282 * This really ought to just set a flag like the hangup handler does,
283 * then check the flag at "safe" times, in case the signal arrives
284 * while something fragile is executing. Better to have a brief period
285 * where display updates don't fit the new size than for tty internals
286 * to become corrupted.
288 * 'winch_seen' has been "notyet" for a long time....
292 winch_handler(sig_unused) /* signal handler is called with at least 1 arg */
293 int sig_unused UNUSED;
295 int oldLI = LI, oldCO = CO, i;
296 register struct WinDesc *cw;
300 #define WINCH_MESSAGE "(SIGWINCH)"
302 (void) write(fileno(wc_tracelogf), WINCH_MESSAGE,
303 strlen(WINCH_MESSAGE));
308 /* For long running events such as multi-page menus and
309 * display_file(), we note the signal's occurance and
310 * hope the code there decides to handle the situation
311 * and reset the flag. There will be race conditions
312 * when handling this - code handlers so it doesn't matter.
317 if ((oldLI != LI || oldCO != CO) && ttyDisplay) {
318 ttyDisplay->rows = LI;
319 ttyDisplay->cols = CO;
321 cw = wins[BASE_WINDOW];
322 cw->rows = ttyDisplay->rows;
323 cw->cols = ttyDisplay->cols;
325 if (iflags.window_inited) {
326 cw = wins[WIN_MESSAGE];
327 cw->curx = cw->cury = 0;
331 i = ttyDisplay->toplin;
332 ttyDisplay->toplin = 0;
335 ttyDisplay->toplin = i;
340 for (i = WIN_INVEN; i < MAXWIN; i++)
341 if (wins[i] && wins[i]->active) {
343 addtopl("Press Return to continue: ");
346 (void) fflush(stdout);
355 /* destroy and recreate status window; extracted from winch_handler()
356 and augmented for use by tty_preference_update() */
360 if (WIN_STATUS != WIN_ERR) {
361 /* if it's shrinking, clear it before destroying so that
362 dropped portion won't show anything that's now becoming stale */
363 if (wins[WIN_STATUS]->maxrow > iflags.wc2_statuslines)
364 tty_clear_nhwindow(WIN_STATUS);
366 tty_destroy_nhwindow(WIN_STATUS), WIN_STATUS = WIN_ERR;
368 /* frees some status tracking data */
369 genl_status_finish();
370 /* creates status window and allocates tracking data */
372 tty_clear_nhwindow(WIN_STATUS); /* does some init, sets context.botlx */
373 #ifdef STATUS_HILITES
374 status_initialize(REASSESS_ONLY);
379 if (LI < 1 + ROWNO + iflags.wc2_statuslines) {
381 tty_cliparound(u.ux, u.uy);
392 tty_init_nhwindows(argcp, argv)
398 /* options aren't processed yet so wc2_statuslines might be 0;
399 make sure that it has a reasonable value during tty setup */
400 iflags.wc2_statuslines = (iflags.wc2_statuslines < 3) ? 2 : 3;
402 * Remember tty modes, to be restored on exit.
404 * gettty() must be called before tty_startup()
405 * due to ordering of LI/CO settings
406 * tty_startup() must be called before initoptions()
407 * due to ordering of graphics settings
409 #if defined(UNIX) || defined(VMS)
410 setbuf(stdout, obuf);
414 /* to port dependant tty setup */
415 tty_startup(&wid, &hgt);
416 setftty(); /* calls start_screen */
418 /* set up tty descriptor */
419 ttyDisplay = (struct DisplayDesc *) alloc(sizeof (struct DisplayDesc));
420 ttyDisplay->toplin = 0;
421 ttyDisplay->rows = hgt;
422 ttyDisplay->cols = wid;
423 ttyDisplay->curx = ttyDisplay->cury = 0;
424 ttyDisplay->inmore = ttyDisplay->inread = ttyDisplay->intr = 0;
425 ttyDisplay->dismiss_more = 0;
427 ttyDisplay->color = NO_COLOR;
429 ttyDisplay->attrs = 0;
431 /* set up the default windows */
432 BASE_WINDOW = tty_create_nhwindow(NHW_BASE);
433 wins[BASE_WINDOW]->active = 1;
435 ttyDisplay->lastwin = WIN_ERR;
437 #if defined(SIGWINCH) && defined(CLIPPING) && !defined(NO_SIGNAL)
438 (void) signal(SIGWINCH, (SIG_RET_TYPE) winch_handler);
441 tty_clear_nhwindow(BASE_WINDOW);
443 tty_putstr(BASE_WINDOW, 0, "");
444 for (i = 1; i <= 4; ++i)
445 tty_putstr(BASE_WINDOW, 0, copyright_banner_line(i));
446 tty_putstr(BASE_WINDOW, 0, "");
447 #if 0 /*JP*//*JPTB reversed*/
448 tty_putstr(BASE_WINDOW, 0, JA_COPYRIGHT_BANNER_A);
449 tty_putstr(BASE_WINDOW, 0, JA_COPYRIGHT_BANNER_B);
450 tty_putstr(BASE_WINDOW, 0, JA_COPYRIGHT_BANNER_C);
451 tty_putstr(BASE_WINDOW, 0, JA_COPYRIGHT_BANNER_D);
452 tty_putstr(BASE_WINDOW, 0, "");
454 tty_display_nhwindow(BASE_WINDOW, FALSE);
456 /* 'statuslines' defaults to SET_IN_FILE, allowed but invisible;
457 make it dynamically settable if feasible, otherwise visible */
458 if (tty_procs.wincap2 & WC2_STATUSLINES)
459 set_wc2_option_mod_status(WC2_STATUSLINES,
461 (LI < 1 + ROWNO + 2) ? DISP_IN_GAME :
467 tty_preference_update(pref)
470 if (!strcmp(pref, "statuslines") && iflags.window_inited) {
475 nttty_preference_update(pref);
477 genl_preference_update(pref);
482 /* try to reduce clutter in the code below... */
483 #define ROLE flags.initrole
484 #define RACE flags.initrace
485 #define GEND flags.initgend
486 #define ALGN flags.initalign
489 tty_player_selection()
491 int i, k, n, choice, nextpick;
492 boolean getconfirmation, picksomething;
494 char pbuf[QBUFSZ], plbuf[QBUFSZ];
497 menu_item *selected = 0;
499 /* Used to avoid "Is this ok?" if player has already specified all
500 * four facets of role.
501 * Note that rigid_role_checks might force any unspecified facets to
502 * have a specific value, but that will still require confirmation;
503 * player can specify the forced ones if avoiding that is demanded.
505 picksomething = (ROLE == ROLE_NONE || RACE == ROLE_NONE
506 || GEND == ROLE_NONE || ALGN == ROLE_NONE);
508 * choose randomly without asking for all unspecified facets.
510 if (flags.randomall && picksomething) {
511 if (ROLE == ROLE_NONE)
513 if (RACE == ROLE_NONE)
515 if (GEND == ROLE_NONE)
517 if (ALGN == ROLE_NONE)
521 /* prevent unnecessary prompting if role forces race (samurai) or gender
522 (valkyrie) or alignment (rogue), or race forces alignment (orc), &c */
525 /* Should we randomly pick for the player? */
526 if (ROLE == ROLE_NONE || RACE == ROLE_NONE || GEND == ROLE_NONE
527 || ALGN == ROLE_NONE) {
529 char *prompt = build_plselection_prompt(pbuf, QBUFSZ,
530 ROLE, RACE, GEND, ALGN);
532 /* this prompt string ends in "[ynaq]?":
533 y - game picks role,&c then asks player to confirm;
534 n - player manually chooses via menu selections;
535 a - like 'y', but skips confirmation and starts game;
538 tty_putstr(BASE_WINDOW, 0, "");
539 echoline = wins[BASE_WINDOW]->cury;
540 tty_putstr(BASE_WINDOW, 0, prompt);
542 pick4u = lowc(readchar());
543 if (index(quitchars, pick4u))
545 } while (!index(ynaqchars, pick4u));
546 if ((int) strlen(prompt) + 1 < CO) {
547 /* Echo choice and move back down line */
548 tty_putsym(BASE_WINDOW, (int) strlen(prompt) + 1, echoline,
550 tty_putstr(BASE_WINDOW, 0, "");
552 /* Otherwise it's hard to tell where to echo, and things are
553 * wrapping a bit messily anyway, so (try to) make sure the next
554 * question shows up well and doesn't get wrapped at the
555 * bottom of the window.
557 tty_clear_nhwindow(BASE_WINDOW);
559 if (pick4u != 'y' && pick4u != 'a' && pick4u != 'n')
566 if (nextpick == RS_ROLE) {
568 /* Select a role, if necessary;
569 we'll try to be compatible with pre-selected
570 race/gender/alignment, but may not succeed. */
572 /* Process the choice */
573 if (pick4u == 'y' || pick4u == 'a' || ROLE == ROLE_RANDOM) {
574 /* Pick a random role */
575 k = pick_role(RACE, GEND, ALGN, PICK_RANDOM);
577 tty_putstr(BASE_WINDOW, 0, "Incompatible role!");
581 /* Prompt for a role */
582 tty_clear_nhwindow(BASE_WINDOW);
583 role_selection_prolog(RS_ROLE, BASE_WINDOW);
584 win = create_nhwindow(NHW_MENU);
586 /* populate the menu with role choices */
587 setup_rolemenu(win, TRUE, RACE, GEND, ALGN);
588 /* add miscellaneous menu entries */
589 role_menu_extra(ROLE_RANDOM, win, TRUE);
590 any = zeroany; /* separator, not a choice */
591 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
593 role_menu_extra(RS_RACE, win, FALSE);
594 role_menu_extra(RS_GENDER, win, FALSE);
595 role_menu_extra(RS_ALGNMNT, win, FALSE);
597 role_menu_extra(RS_filter, win, FALSE);
598 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
600 Strcpy(pbuf, "Pick a role or profession");
602 Strcpy(pbuf, "
\90E
\8bÆ
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢");
604 n = select_menu(win, PICK_ONE, &selected);
606 * PICK_ONE with preselected choice behaves strangely:
607 * n == -1 -- <escape>, so use quit choice;
608 * n == 0 -- explicitly chose preselected entry,
609 * toggling it off, so use it;
610 * n == 1 -- implicitly chose preselected entry
611 * with <space> or <return>;
612 * n == 2 -- explicitly chose a different entry, so
613 * both it and preselected one are in list.
616 choice = selected[0].item.a_int;
617 if (n > 1 && choice == ROLE_RANDOM)
618 choice = selected[1].item.a_int;
620 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
622 free((genericptr_t) selected), selected = 0;
623 destroy_nhwindow(win);
625 if (choice == ROLE_NONE) {
626 goto give_up; /* Selected quit */
627 } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
628 ALGN = k = ROLE_NONE;
629 nextpick = RS_ALGNMNT;
630 } else if (choice == RS_menu_arg(RS_GENDER)) {
631 GEND = k = ROLE_NONE;
632 nextpick = RS_GENDER;
633 } else if (choice == RS_menu_arg(RS_RACE)) {
634 RACE = k = ROLE_NONE;
636 } else if (choice == RS_menu_arg(RS_filter)) {
637 ROLE = k = ROLE_NONE;
638 (void) reset_role_filtering();
640 } else if (choice == ROLE_RANDOM) {
641 k = pick_role(RACE, GEND, ALGN, PICK_RANDOM);
652 if (nextpick == RS_RACE) {
653 nextpick = (ROLE < 0) ? RS_ROLE : RS_GENDER;
654 /* Select a race, if necessary;
655 force compatibility with role, try for compatibility
656 with pre-selected gender/alignment. */
657 if (RACE < 0 || !validrace(ROLE, RACE)) {
658 /* no race yet, or pre-selected race not valid */
659 if (pick4u == 'y' || pick4u == 'a' || RACE == ROLE_RANDOM) {
660 k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM);
662 tty_putstr(BASE_WINDOW, 0, "Incompatible race!");
665 } else { /* pick4u == 'n' */
666 /* Count the number of valid races */
667 n = 0; /* number valid */
668 k = 0; /* valid race */
669 for (i = 0; races[i].noun; i++)
670 if (ok_race(ROLE, i, GEND, ALGN)) {
675 for (i = 0; races[i].noun; i++)
676 if (validrace(ROLE, i)) {
681 /* Permit the user to pick, if there is more than one */
683 tty_clear_nhwindow(BASE_WINDOW);
684 role_selection_prolog(RS_RACE, BASE_WINDOW);
685 win = create_nhwindow(NHW_MENU);
687 any = zeroany; /* zero out all bits */
688 /* populate the menu with role choices */
689 setup_racemenu(win, TRUE, ROLE, GEND, ALGN);
690 /* add miscellaneous menu entries */
691 role_menu_extra(ROLE_RANDOM, win, TRUE);
692 any.a_int = 0; /* separator, not a choice */
693 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
695 role_menu_extra(RS_ROLE, win, FALSE);
696 role_menu_extra(RS_GENDER, win, FALSE);
697 role_menu_extra(RS_ALGNMNT, win, FALSE);
699 role_menu_extra(RS_filter, win, FALSE);
700 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
702 Strcpy(pbuf, "Pick a race or species");
704 Strcpy(pbuf, "
\8eí
\91°
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢");
706 n = select_menu(win, PICK_ONE, &selected);
708 choice = selected[0].item.a_int;
709 if (n > 1 && choice == ROLE_RANDOM)
710 choice = selected[1].item.a_int;
712 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
714 free((genericptr_t) selected), selected = 0;
715 destroy_nhwindow(win);
717 if (choice == ROLE_NONE) {
718 goto give_up; /* Selected quit */
719 } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
720 ALGN = k = ROLE_NONE;
721 nextpick = RS_ALGNMNT;
722 } else if (choice == RS_menu_arg(RS_GENDER)) {
723 GEND = k = ROLE_NONE;
724 nextpick = RS_GENDER;
725 } else if (choice == RS_menu_arg(RS_ROLE)) {
726 ROLE = k = ROLE_NONE;
728 } else if (choice == RS_menu_arg(RS_filter)) {
729 RACE = k = ROLE_NONE;
730 if (reset_role_filtering())
734 } else if (choice == ROLE_RANDOM) {
735 k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM);
747 if (nextpick == RS_GENDER) {
748 nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE
750 /* Select a gender, if necessary;
751 force compatibility with role/race, try for compatibility
752 with pre-selected alignment. */
753 if (GEND < 0 || !validgend(ROLE, RACE, GEND)) {
754 /* no gender yet, or pre-selected gender not valid */
755 if (pick4u == 'y' || pick4u == 'a' || GEND == ROLE_RANDOM) {
756 k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM);
758 tty_putstr(BASE_WINDOW, 0, "Incompatible gender!");
759 k = randgend(ROLE, RACE);
761 } else { /* pick4u == 'n' */
762 /* Count the number of valid genders */
763 n = 0; /* number valid */
764 k = 0; /* valid gender */
765 for (i = 0; i < ROLE_GENDERS; i++)
766 if (ok_gend(ROLE, RACE, i, ALGN)) {
771 for (i = 0; i < ROLE_GENDERS; i++)
772 if (validgend(ROLE, RACE, i)) {
777 /* Permit the user to pick, if there is more than one */
779 tty_clear_nhwindow(BASE_WINDOW);
780 role_selection_prolog(RS_GENDER, BASE_WINDOW);
781 win = create_nhwindow(NHW_MENU);
783 any = zeroany; /* zero out all bits */
784 /* populate the menu with gender choices */
785 setup_gendmenu(win, TRUE, ROLE, RACE, ALGN);
786 /* add miscellaneous menu entries */
787 role_menu_extra(ROLE_RANDOM, win, TRUE);
788 any.a_int = 0; /* separator, not a choice */
789 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
791 role_menu_extra(RS_ROLE, win, FALSE);
792 role_menu_extra(RS_RACE, win, FALSE);
793 role_menu_extra(RS_ALGNMNT, win, FALSE);
795 role_menu_extra(RS_filter, win, FALSE);
796 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
798 Strcpy(pbuf, "Pick a gender or sex");
800 Strcpy(pbuf, "
\90«
\95Ê
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢");
802 n = select_menu(win, PICK_ONE, &selected);
804 choice = selected[0].item.a_int;
805 if (n > 1 && choice == ROLE_RANDOM)
806 choice = selected[1].item.a_int;
808 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
810 free((genericptr_t) selected), selected = 0;
811 destroy_nhwindow(win);
813 if (choice == ROLE_NONE) {
814 goto give_up; /* Selected quit */
815 } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
816 ALGN = k = ROLE_NONE;
817 nextpick = RS_ALGNMNT;
818 } else if (choice == RS_menu_arg(RS_RACE)) {
819 RACE = k = ROLE_NONE;
821 } else if (choice == RS_menu_arg(RS_ROLE)) {
822 ROLE = k = ROLE_NONE;
824 } else if (choice == RS_menu_arg(RS_filter)) {
825 GEND = k = ROLE_NONE;
826 if (reset_role_filtering())
829 nextpick = RS_GENDER;
830 } else if (choice == ROLE_RANDOM) {
831 k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM);
833 k = randgend(ROLE, RACE);
840 } /* needed gender */
841 } /* picking gender */
843 if (nextpick == RS_ALGNMNT) {
844 nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE : RS_GENDER;
845 /* Select an alignment, if necessary;
846 force compatibility with role/race/gender. */
847 if (ALGN < 0 || !validalign(ROLE, RACE, ALGN)) {
848 /* no alignment yet, or pre-selected alignment not valid */
849 if (pick4u == 'y' || pick4u == 'a' || ALGN == ROLE_RANDOM) {
850 k = pick_align(ROLE, RACE, GEND, PICK_RANDOM);
852 tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!");
853 k = randalign(ROLE, RACE);
855 } else { /* pick4u == 'n' */
856 /* Count the number of valid alignments */
857 n = 0; /* number valid */
858 k = 0; /* valid alignment */
859 for (i = 0; i < ROLE_ALIGNS; i++)
860 if (ok_align(ROLE, RACE, GEND, i)) {
865 for (i = 0; i < ROLE_ALIGNS; i++)
866 if (validalign(ROLE, RACE, i)) {
871 /* Permit the user to pick, if there is more than one */
873 tty_clear_nhwindow(BASE_WINDOW);
874 role_selection_prolog(RS_ALGNMNT, BASE_WINDOW);
875 win = create_nhwindow(NHW_MENU);
877 any = zeroany; /* zero out all bits */
878 setup_algnmenu(win, TRUE, ROLE, RACE, GEND);
879 role_menu_extra(ROLE_RANDOM, win, TRUE);
880 any.a_int = 0; /* separator, not a choice */
881 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
883 role_menu_extra(RS_ROLE, win, FALSE);
884 role_menu_extra(RS_RACE, win, FALSE);
885 role_menu_extra(RS_GENDER, win, FALSE);
887 role_menu_extra(RS_filter, win, FALSE);
888 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
890 Strcpy(pbuf, "Pick an alignment or creed");
892 Strcpy(pbuf, "
\91®
\90«
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢");
894 n = select_menu(win, PICK_ONE, &selected);
896 choice = selected[0].item.a_int;
897 if (n > 1 && choice == ROLE_RANDOM)
898 choice = selected[1].item.a_int;
900 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
902 free((genericptr_t) selected), selected = 0;
903 destroy_nhwindow(win);
905 if (choice == ROLE_NONE) {
906 goto give_up; /* Selected quit */
907 } else if (choice == RS_menu_arg(RS_GENDER)) {
908 GEND = k = ROLE_NONE;
909 nextpick = RS_GENDER;
910 } else if (choice == RS_menu_arg(RS_RACE)) {
911 RACE = k = ROLE_NONE;
913 } else if (choice == RS_menu_arg(RS_ROLE)) {
914 ROLE = k = ROLE_NONE;
916 } else if (choice == RS_menu_arg(RS_filter)) {
917 ALGN = k = ROLE_NONE;
918 if (reset_role_filtering())
921 nextpick = RS_ALGNMNT;
922 } else if (choice == ROLE_RANDOM) {
923 k = pick_align(ROLE, RACE, GEND, PICK_RANDOM);
925 k = randalign(ROLE, RACE);
932 } /* needed alignment */
933 } /* picking alignment */
935 } while (ROLE < 0 || RACE < 0 || GEND < 0 || ALGN < 0);
938 * Role, race, &c have now been determined;
939 * ask for confirmation and maybe go back to choose all over again.
941 * Uses ynaq for familiarity, although 'a' is usually a
942 * superset of 'y' but here is an alternate form of 'n'.
944 * title: Is this ok? [ynaq]
946 * text: $name, $alignment $gender $race $role
948 * menu: y + yes; play
950 * maybe: a - no; rename hero
954 getconfirmation = (picksomething && pick4u != 'a' && !flags.randomall);
955 while (getconfirmation) {
956 tty_clear_nhwindow(BASE_WINDOW);
957 role_selection_prolog(ROLE_NONE, BASE_WINDOW);
958 win = create_nhwindow(NHW_MENU);
960 any = zeroany; /* zero out all bits */
962 if (!roles[ROLE].name.f
963 && (roles[ROLE].allow & ROLE_GENDMASK)
964 == (ROLE_MALE | ROLE_FEMALE))
966 Sprintf(plbuf, " %s", genders[GEND].adj);
968 Sprintf(plbuf, "%s
\82Ì", genders[GEND].adj);
970 *plbuf = '\0'; /* omit redundant gender */
972 Sprintf(pbuf, "%s, %s%s %s %s", plname, aligns[ALGN].adj, plbuf,
974 (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f
975 : roles[ROLE].name.m);
977 Sprintf(pbuf, "%s, %s%s%s%s", plname, aligns[ALGN].adj, plbuf,
979 (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f
980 : roles[ROLE].name.m);
982 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, pbuf,
984 /* blank separator */
986 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
987 /* [ynaq] menu choices */
990 add_menu(win, NO_GLYPH, &any, 'y', 0, ATR_NONE, "Yes; start game",
992 add_menu(win, NO_GLYPH, &any, 'y', 0, ATR_NONE, "
\82Í
\82¢;
\83Q
\81[
\83\80\82ð
\8en
\82ß
\82é",
995 add_menu(win, NO_GLYPH, &any, 'n', 0, ATR_NONE,
997 "No; choose role again", MENU_UNSELECTED);
999 "
\82¢
\82¢
\82¦;
\90E
\8bÆ
\82ð
\91I
\82Ñ
\92¼
\82·", MENU_UNSELECTED);
1000 if (iflags.renameallowed) {
1002 add_menu(win, NO_GLYPH, &any, 'a', 0, ATR_NONE,
1004 "Not yet; choose another name", MENU_UNSELECTED);
1006 "
\82Ü
\82¾;
\96¼
\91O
\82ð
\95Ï
\82¦
\82é", MENU_UNSELECTED);
1010 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
1012 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "
\94²
\82¯
\82é",
1015 Sprintf(pbuf, "Is this ok? [yn%sq]", iflags.renameallowed ? "a" : "");
1017 Sprintf(pbuf, "
\82±
\82ê
\82Å
\82æ
\82¢
\81H [yn%sq]", iflags.renameallowed ? "a" : "");
1018 end_menu(win, pbuf);
1019 n = select_menu(win, PICK_ONE, &selected);
1020 /* [pick-one menus with a preselected entry behave oddly...] */
1021 choice = (n > 0) ? selected[n - 1].item.a_int : (n == 0) ? 1 : -1;
1023 free((genericptr_t) selected), selected = 0;
1024 destroy_nhwindow(win);
1027 default: /* 'q' or ESC */
1028 goto give_up; /* quit */
1032 * TODO: what, if anything, should be done if the name is
1033 * changed to or from "wizard" after port-specific startup
1034 * code has set flags.debug based on the original name?
1036 int saveROLE, saveRACE, saveGEND, saveALGN;
1038 iflags.renameinprogress = TRUE;
1039 /* plnamesuffix() can change any or all of ROLE, RACE,
1040 GEND, ALGN; we'll override that and honor only the name */
1041 saveROLE = ROLE, saveRACE = RACE, saveGEND = GEND,
1044 plnamesuffix(); /* calls askname() when plname[] is empty */
1045 ROLE = saveROLE, RACE = saveRACE, GEND = saveGEND,
1047 break; /* getconfirmation is still True */
1050 /* start fresh, but bypass "shall I pick everything for you?"
1051 step; any partial role selection via config file, command
1052 line, or name suffix is discarded this time */
1054 ROLE = RACE = GEND = ALGN = ROLE_NONE;
1057 case 1: /* 'y' or Space or Return/Enter */
1058 /* success; drop out through end of function */
1059 getconfirmation = FALSE;
1065 tty_display_nhwindow(BASE_WINDOW, FALSE);
1071 free((genericptr_t) selected); /* [obsolete] */
1078 reset_role_filtering()
1083 menu_item *selected = 0;
1085 win = create_nhwindow(NHW_MENU);
1089 /* no extra blank line preceding this entry; end_menu supplies one */
1090 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
1091 "Unacceptable roles", MENU_UNSELECTED);
1092 setup_rolemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
1094 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1095 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
1096 "Unacceptable races", MENU_UNSELECTED);
1097 setup_racemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
1099 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1100 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
1101 "Unacceptable genders", MENU_UNSELECTED);
1102 setup_gendmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
1104 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1105 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
1106 "Unacceptable alignments", MENU_UNSELECTED);
1107 setup_algnmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
1109 end_menu(win, "Pick all that apply");
1110 n = select_menu(win, PICK_ANY, &selected);
1114 for (i = 0; i < n; i++)
1115 setrolefilter(selected[i].item.a_string);
1117 ROLE = RACE = GEND = ALGN = ROLE_NONE;
1120 free((genericptr_t) selected), selected = 0;
1121 destroy_nhwindow(win);
1122 return (n > 0) ? TRUE : FALSE;
1130 /* add entries a-Archeologist, b-Barbarian, &c to menu being built in 'win' */
1132 setup_rolemenu(win, filtering, race, gend, algn)
1134 boolean filtering; /* True => exclude filtered roles; False => filter reset */
1135 int race, gend, algn; /* all ROLE_NONE for !filtering case */
1140 char thisch, lastch = '\0', rolenamebuf[50];
1142 any = zeroany; /* zero out all bits */
1143 for (i = 0; roles[i].name.m; i++) {
1144 role_ok = ok_role(i, race, gend, algn);
1145 if (filtering && !role_ok)
1150 any.a_string = roles[i].name.m;
1152 thisch = lowc(*roles[i].name.m);
1154 thisch = lowc(*roles[i].filecode);
1155 if (thisch == lastch)
1156 thisch = highc(thisch);
1157 Strcpy(rolenamebuf, roles[i].name.m);
1158 if (roles[i].name.f) {
1159 /* role has distinct name for female (C,P) */
1161 /* female already chosen; replace male name */
1162 Strcpy(rolenamebuf, roles[i].name.f);
1163 } else if (gend < 0) {
1164 /* not chosen yet; append slash+female name */
1165 Strcat(rolenamebuf, "/");
1166 Strcat(rolenamebuf, roles[i].name.f);
1169 /* !filtering implies reset_role_filtering() where we want to
1170 mark this role as preseleted if current filter excludes it */
1171 add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE, an(rolenamebuf),
1172 (!filtering && !role_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1178 setup_racemenu(win, filtering, role, gend, algn)
1181 int role, gend, algn;
1189 for (i = 0; races[i].noun; i++) {
1190 race_ok = ok_race(role, i, gend, algn);
1191 if (filtering && !race_ok)
1196 any.a_string = races[i].noun;
1198 this_ch = *races[i].noun;
1200 this_ch = lowc(*races[i].filecode);
1201 /* filtering: picking race, so choose by first letter, with
1202 capital letter as unseen accelerator;
1203 !filtering: resetting filter rather than picking, choose by
1204 capital letter since lowercase role letters will be present */
1205 add_menu(win, NO_GLYPH, &any,
1206 filtering ? this_ch : highc(this_ch),
1207 filtering ? highc(this_ch) : 0,
1208 ATR_NONE, races[i].noun,
1209 (!filtering && !race_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1214 setup_gendmenu(win, filtering, role, race, algn)
1217 int role, race, algn;
1225 for (i = 0; i < ROLE_GENDERS; i++) {
1226 gend_ok = ok_gend(role, race, i, algn);
1227 if (filtering && !gend_ok)
1232 any.a_string = genders[i].adj;
1234 this_ch = *genders[i].adj;
1236 this_ch = lowc(*genders[i].filecode);
1237 /* (see setup_racemenu for explanation of selector letters
1238 and setup_rolemenu for preselection) */
1239 add_menu(win, NO_GLYPH, &any,
1240 filtering ? this_ch : highc(this_ch),
1241 filtering ? highc(this_ch) : 0,
1242 ATR_NONE, genders[i].adj,
1243 (!filtering && !gend_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1248 setup_algnmenu(win, filtering, role, race, gend)
1251 int role, race, gend;
1259 for (i = 0; i < ROLE_ALIGNS; i++) {
1260 algn_ok = ok_align(role, race, gend, i);
1261 if (filtering && !algn_ok)
1267 any.a_string = aligns[i].adj;
1269 any.a_string = aligns[i].noun;
1271 this_ch = *aligns[i].adj;
1273 this_ch = lowc(*aligns[i].filecode);
1274 /* (see setup_racemenu for explanation of selector letters
1275 and setup_rolemenu for preselection) */
1277 add_menu(win, NO_GLYPH, &any,
1278 filtering ? this_ch : highc(this_ch),
1279 filtering ? highc(this_ch) : 0,
1280 ATR_NONE, aligns[i].adj,
1281 (!filtering && !algn_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1283 add_menu(win, NO_GLYPH, &any,
1284 filtering ? this_ch : highc(this_ch),
1285 filtering ? highc(this_ch) : 0,
1286 ATR_NONE, aligns[i].noun,
1287 (!filtering && !algn_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1293 * plname is filled either by an option (-u Player or -uPlayer) or
1294 * explicitly (by being the wizard) or by askname.
1295 * It may still contain a suffix denoting the role, etc.
1296 * Always called after init_nhwindows() and before display_gamewindows().
1302 static const char who_are_you[] = "Who are you? ";
1304 static const char who_are_you[] = "
\82 \82È
\82½
\82Í
\92N
\81H ";
1305 register int c, ct, tryct = 0;
1307 char ptmpname[PL_NSIZ];
1311 if (iflags.wc2_selectsaved && !iflags.renameinprogress)
1312 switch (restore_menu(BASE_WINDOW)) {
1314 bail("Until next time then..."); /* quit */
1317 break; /* no game chosen; start new game */
1319 return; /* plname[] has been set */
1321 #endif /* SELECTSAVED */
1323 tty_putstr(BASE_WINDOW, 0, "");
1327 bail("Giving up after 10 tries.\n");
1328 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury - 1);
1330 tty_putstr(BASE_WINDOW, 0, "Enter a name for your character...");
1332 tty_putstr(BASE_WINDOW, 0, "
\82 \82È
\82½
\82Ì
\83L
\83\83\83\89\83N
\83^
\82Ì
\96¼
\91O
\82Í
\81H");
1334 /* erase previous prompt (in case of ESC after partial response) */
1335 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury), cl_end();
1337 tty_putstr(BASE_WINDOW, 0, who_are_you);
1338 tty_curs(BASE_WINDOW, (int) (sizeof who_are_you),
1339 wins[BASE_WINDOW]->cury - 1);
1341 while ((c = tty_nhgetch()) != '\n') {
1349 } /* continue outer loop */
1350 #if defined(WIN32CON)
1352 bail("^C abort.\n");
1354 /* some people get confused when their erase char is not ^H */
1355 if (c == '\b' || c == '\177') {
1364 #if defined(MICRO) || defined(WIN32CON)
1365 #if defined(WIN32CON) || defined(MSDOS)
1366 backsp(); /* \b is visible on NT */
1367 (void) putchar(' ');
1373 (void) putchar('\b');
1374 (void) putchar(' ');
1375 (void) putchar('\b');
1379 if(is_kanji2(ptmpname, ct))
1384 #if defined(UNIX) || defined(VMS)
1386 if (c != '-' && c != '@')
1387 if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z')
1388 /* reject leading digit but allow digits elsewhere
1389 (avoids ambiguity when character name gets
1390 appended to uid to construct save file name) */
1391 && !(c >= '0' && c <= '9' && ct > 0))
1395 if (ct < (int) (sizeof plname) - 1) {
1398 if (iflags.grmode) {
1405 wins[BASE_WINDOW]->curx++;
1434 /* move to next line to simulate echo of user's <return> */
1435 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury + 1);
1437 Strcpy(plname, str2ic(ptmpname));
1440 /* since we let user pick an arbitrary name now, he/she can pick
1441 another one during role selection */
1442 iflags.renameallowed = TRUE;
1451 #if !defined(MICRO) && !defined(WIN32CON)
1463 xputs(iflags.cbreak ? "space" : "return");
1464 xputs(" to continue: ");
1472 tty_suspend_nhwindows(str)
1475 settty(str); /* calls end_screen, perhaps raw_print */
1477 tty_raw_print(""); /* calls fflush(stdout) */
1481 tty_resume_nhwindows()
1484 setftty(); /* calls start_screen */
1489 tty_exit_nhwindows(str)
1494 tty_suspend_nhwindows(str);
1496 * Disable windows to avoid calls to window routines.
1498 free_pickinv_cache(); /* reset its state as well as tear down window */
1499 for (i = 0; i < MAXWIN; i++) {
1500 if (i == BASE_WINDOW)
1501 continue; /* handle wins[BASE_WINDOW] last */
1503 #ifdef FREE_ALL_MEMORY
1504 free_window_info(wins[i], TRUE);
1505 free((genericptr_t) wins[i]);
1507 wins[i] = (struct WinDesc *) 0;
1510 WIN_MAP = WIN_MESSAGE = WIN_INVEN = WIN_ERR; /* these are all gone now */
1511 WIN_STATUS = WIN_ERR;
1512 #ifdef FREE_ALL_MEMORY
1513 if (BASE_WINDOW != WIN_ERR && wins[BASE_WINDOW]) {
1514 free_window_info(wins[BASE_WINDOW], TRUE);
1515 free((genericptr_t) wins[BASE_WINDOW]);
1516 wins[BASE_WINDOW] = (struct WinDesc *) 0;
1517 BASE_WINDOW = WIN_ERR;
1519 free((genericptr_t) ttyDisplay);
1520 ttyDisplay = (struct DisplayDesc *) 0;
1523 #ifndef NO_TERMS /*(until this gets added to the window interface)*/
1524 tty_shutdown(); /* cleanup termcap/terminfo/whatever */
1529 iflags.window_inited = 0;
1533 tty_create_nhwindow(type)
1536 struct WinDesc *newwin;
1540 for (newid = 0; newid < MAXWIN; ++newid)
1541 if (wins[newid] == 0)
1543 if (newid == MAXWIN) {
1544 panic("No window slots!");
1549 newwin = (struct WinDesc *) alloc(sizeof (struct WinDesc));
1550 wins[newid] = newwin;
1552 newwin->type = type;
1554 newwin->active = FALSE;
1555 newwin->curx = newwin->cury = 0;
1556 newwin->morestr = 0;
1557 newwin->mlist = (tty_menu_item *) 0;
1558 newwin->plist = (tty_menu_item **) 0;
1559 newwin->npages = newwin->plist_size = newwin->nitems = newwin->how = 0;
1562 /* base window, used for absolute movement on the screen */
1563 newwin->offx = newwin->offy = 0;
1564 newwin->rows = ttyDisplay->rows;
1565 newwin->cols = ttyDisplay->cols;
1566 newwin->maxrow = newwin->maxcol = 0;
1569 /* message window, 1 line long, very wide, top of screen */
1570 newwin->offx = newwin->offy = 0;
1572 if (iflags.msg_history < 20)
1573 iflags.msg_history = 20;
1574 else if (iflags.msg_history > 60)
1575 iflags.msg_history = 60;
1576 newwin->maxrow = newwin->rows = iflags.msg_history;
1577 newwin->maxcol = newwin->cols = 0;
1580 /* status window, 2 or 3 lines long, full width, bottom of screen */
1581 if (iflags.wc2_statuslines < 2
1583 || (LI < 1 + ROWNO + 3)
1585 || iflags.wc2_statuslines > 3)
1586 iflags.wc2_statuslines = 2;
1588 rowoffset = ttyDisplay->rows - iflags.wc2_statuslines;
1589 #if defined(USE_TILES) && defined(MSDOS)
1590 if (iflags.grmode) {
1591 newwin->offy = rowoffset;
1594 newwin->offy = min(rowoffset, ROWNO + 1);
1595 newwin->rows = newwin->maxrow = iflags.wc2_statuslines;
1596 newwin->cols = newwin->maxcol = ttyDisplay->cols;
1599 /* map window, ROWNO lines long, full width, below message window */
1602 newwin->rows = ROWNO;
1603 newwin->cols = COLNO;
1604 newwin->maxrow = 0; /* no buffering done -- let gbuf do it */
1609 /* inventory/menu window, variable length, full width, top of screen;
1610 help window, the same, different semantics for display, etc */
1611 newwin->offx = newwin->offy = 0;
1613 newwin->cols = ttyDisplay->cols;
1614 newwin->maxrow = newwin->maxcol = 0;
1617 panic("Tried to create window type %d\n", (int) type);
1622 if (newwin->maxrow) {
1623 newwin->data = (char **) alloc(
1624 (unsigned) (newwin->maxrow * sizeof (char *)));
1625 newwin->datlen = (short *) alloc(
1626 (unsigned) (newwin->maxrow * sizeof (short)));
1627 for (i = 0; i < newwin->maxrow; i++) {
1628 if (newwin->maxcol) { /* WIN_STATUS */
1629 newwin->data[i] = (char *) alloc((unsigned) newwin->maxcol);
1630 newwin->datlen[i] = (short) newwin->maxcol;
1632 newwin->data[i] = (char *) 0;
1633 newwin->datlen[i] = 0;
1636 if (newwin->type == NHW_MESSAGE)
1639 newwin->data = (char **) 0;
1640 newwin->datlen = (short *) 0;
1647 erase_menu_or_text(window, cw, clear)
1652 if (cw->offx == 0) {
1654 tty_curs(window, 1, 0);
1662 docorner((int) cw->offx, cw->maxrow + 1);
1667 free_window_info(cw, free_data)
1674 if (cw == wins[WIN_MESSAGE] && cw->rows > cw->maxrow)
1675 cw->maxrow = cw->rows; /* topl data */
1676 for (i = 0; i < cw->maxrow; i++)
1678 free((genericptr_t) cw->data[i]);
1679 cw->data[i] = (char *) 0;
1684 free((genericptr_t) cw->data);
1685 cw->data = (char **) 0;
1687 free((genericptr_t) cw->datlen);
1688 cw->datlen = (short *) 0;
1692 cw->maxrow = cw->maxcol = 0;
1694 tty_menu_item *temp;
1696 while ((temp = cw->mlist) != 0) {
1697 cw->mlist = temp->next;
1699 free((genericptr_t) temp->str);
1700 free((genericptr_t) temp);
1704 free((genericptr_t) cw->plist);
1707 cw->plist_size = cw->npages = cw->nitems = cw->how = 0;
1709 free((genericptr_t) cw->morestr);
1715 tty_clear_nhwindow(window)
1719 register struct WinDesc *cw = 0;
1722 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1723 panic(winpanicstr, window);
1724 ttyDisplay->lastwin = window;
1726 print_vt_code2(AVTC_SELECT_WINDOW, window);
1730 if (ttyDisplay->toplin) {
1734 docorner(1, cw->cury + 1);
1735 ttyDisplay->toplin = 0;
1741 for (i = 0; i < m; ++i) {
1742 tty_curs(window, 1, i);
1745 for (j = 0; j < n - 1; ++j)
1746 cw->data[i][j] = ' ';
1747 cw->data[i][n - 1] = '\0';
1748 /*finalx[i][NOW] = finalx[i][BEFORE] = 0;*/
1753 /* cheap -- clear the whole thing and tell nethack to redraw botl */
1758 /*for (i = 0; i < cw->maxrow; ++i) */
1759 /* finalx[i][NOW] = finalx[i][BEFORE] = 0;*/
1764 erase_menu_or_text(window, cw, TRUE);
1765 free_window_info(cw, FALSE);
1768 cw->curx = cw->cury = 0;
1772 toggle_menu_curr(window, curr, lineno, in_view, counting, count)
1774 tty_menu_item *curr;
1776 boolean in_view, counting;
1779 if (curr->selected) {
1780 if (counting && count > 0) {
1781 curr->count = count;
1783 set_item_state(window, lineno, curr);
1785 } else { /* change state */
1786 curr->selected = FALSE;
1789 set_item_state(window, lineno, curr);
1792 } else { /* !selected */
1793 if (counting && count > 0) {
1794 curr->count = count;
1795 curr->selected = TRUE;
1797 set_item_state(window, lineno, curr);
1799 } else if (!counting) {
1800 curr->selected = TRUE;
1802 set_item_state(window, lineno, curr);
1805 /* do nothing counting&&count==0 */
1812 register struct WinDesc *cw;
1813 const char *s; /* valid responses */
1815 const char *prompt = cw->morestr ? cw->morestr : defmorestr;
1816 int offset = (cw->type == NHW_TEXT) ? 1 : 2;
1819 tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + offset,
1820 (int) ttyDisplay->cury);
1825 ttyDisplay->curx += strlen(prompt);
1827 jputchar('\0'); /* reset */
1845 set_item_state(window, lineno, item)
1848 tty_menu_item *item;
1850 char ch = item->selected ? (item->count == -1L ? '+' : '#') : '-';
1853 tty_curs(window, 4, lineno);
1854 term_start_attr(item->attr);
1858 (void) jputchar(ch);
1860 term_end_attr(item->attr);
1864 set_all_on_page(window, page_start, page_end)
1866 tty_menu_item *page_start, *page_end;
1868 tty_menu_item *curr;
1871 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1872 if (curr->identifier.a_void && !curr->selected) {
1873 curr->selected = TRUE;
1874 set_item_state(window, n, curr);
1879 unset_all_on_page(window, page_start, page_end)
1881 tty_menu_item *page_start, *page_end;
1883 tty_menu_item *curr;
1886 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1887 if (curr->identifier.a_void && curr->selected) {
1888 curr->selected = FALSE;
1890 set_item_state(window, n, curr);
1895 invert_all_on_page(window, page_start, page_end, acc)
1897 tty_menu_item *page_start, *page_end;
1898 char acc; /* group accelerator, 0 => all */
1900 tty_menu_item *curr;
1903 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1904 if (curr->identifier.a_void && (acc == 0 || curr->gselector == acc)) {
1905 if (curr->selected) {
1906 curr->selected = FALSE;
1909 curr->selected = TRUE;
1910 set_item_state(window, n, curr);
1915 * Invert all entries that match the give group accelerator (or all if zero).
1918 invert_all(window, page_start, page_end, acc)
1920 tty_menu_item *page_start, *page_end;
1921 char acc; /* group accelerator, 0 => all */
1923 tty_menu_item *curr;
1924 boolean on_curr_page;
1925 struct WinDesc *cw = wins[window];
1927 invert_all_on_page(window, page_start, page_end, acc);
1929 /* invert the rest */
1930 for (on_curr_page = FALSE, curr = cw->mlist; curr; curr = curr->next) {
1931 if (curr == page_start)
1932 on_curr_page = TRUE;
1933 else if (curr == page_end)
1934 on_curr_page = FALSE;
1936 if (!on_curr_page && curr->identifier.a_void
1937 && (acc == 0 || curr->gselector == acc)) {
1938 if (curr->selected) {
1939 curr->selected = FALSE;
1942 curr->selected = TRUE;
1947 /* support menucolor in addition to caller-supplied attribute */
1949 toggle_menu_attr(on, color, attr)
1954 term_start_attr(attr);
1956 if (color != NO_COLOR)
1957 term_start_color(color);
1961 if (color != NO_COLOR)
1964 term_end_attr(attr);
1973 process_menu_window(window, cw)
1977 tty_menu_item *page_start, *page_end, *curr;
1979 int n, attr_n, curr_page, page_lines, resp_len;
1980 boolean finished, counting, reset_count;
1981 char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ], *msave, *morestr, really_morc;
1982 #define MENU_EXPLICIT_CHOICE 0x7f /* pseudo menu manipulation char */
1984 curr_page = page_lines = 0;
1985 page_start = page_end = 0;
1986 msave = cw->morestr; /* save the morestr */
1987 cw->morestr = morestr = (char *) alloc((unsigned) QBUFSZ);
1993 /* collect group accelerators; for PICK_NONE, they're ignored;
1994 for PICK_ONE, only those which match exactly one entry will be
1995 accepted; for PICK_ANY, those which match any entry are okay */
1997 if (cw->how != PICK_NONE) {
1999 #define GSELIDX(c) (c & 127) /* guard against `signed char' */
2001 for (i = 0; i < SIZE(gcnt); i++)
2003 for (n = 0, curr = cw->mlist; curr; curr = curr->next)
2004 if (curr->gselector && curr->gselector != curr->selector) {
2006 ++gcnt[GSELIDX(curr->gselector)];
2009 if (n > 0) /* at least one group accelerator found */
2010 for (rp = gacc, curr = cw->mlist; curr; curr = curr->next)
2011 if (curr->gselector && curr->gselector != curr->selector
2012 && !index(gacc, curr->gselector)
2013 && (cw->how == PICK_ANY
2014 || gcnt[GSELIDX(curr->gselector)] == 1)) {
2015 *rp++ = curr->gselector;
2016 *rp = '\0'; /* re-terminate for index() */
2019 resp_len = 0; /* lint suppression */
2021 /* loop until finished */
2031 /* new page to be displayed */
2032 if (curr_page < 0 || (cw->npages > 0 && curr_page >= cw->npages))
2033 panic("bad menu screen page #%d", curr_page);
2036 if (!cw->offx) { /* if not corner, do clearscreen */
2038 tty_curs(window, 1, 0);
2045 if (cw->npages > 0) {
2046 /* collect accelerators */
2047 page_start = cw->plist[curr_page];
2048 page_end = cw->plist[curr_page + 1];
2049 for (page_lines = 0, curr = page_start; curr != page_end;
2050 page_lines++, curr = curr->next) {
2051 int attr, color = NO_COLOR;
2054 *rp++ = curr->selector;
2056 tty_curs(window, 1, page_lines);
2061 (void) putchar(' ');
2063 (void) jputchar(' ');
2067 if (!iflags.use_menu_color
2068 || !get_menu_coloring(curr->str, &color, &attr))
2071 /* which character to start attribute highlighting;
2072 whole line for headers and such, after the selector
2073 character and space and selection indicator for menu
2074 lines (including fake ones that simulate grayed-out
2075 entries, so we don't rely on curr->identifier here) */
2076 attr_n = 0; /* whole line */
2077 if (curr->str[0] && curr->str[1] == ' '
2078 && curr->str[2] && index("-+#", curr->str[2])
2079 && curr->str[3] == ' ')
2080 /* [0]=letter, [1]==space, [2]=[-+#], [3]=space */
2081 attr_n = 4; /* [4:N]=entry description */
2084 * Don't use xputs() because (1) under unix it calls
2085 * tputstr() which will interpret a '*' as some kind
2086 * of padding information and (2) it calls xputc to
2087 * actually output the character. We're faster doing
2090 for (n = 0, cp = curr->str;
2093 (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
2095 (int) ttyDisplay->curx < (int) ttyDisplay->cols;
2099 if (n == attr_n && (color != NO_COLOR
2100 || attr != ATR_NONE))
2101 toggle_menu_attr(TRUE, color, attr);
2103 && curr->identifier.a_void != 0
2104 && curr->selected) {
2105 if (curr->count == -1L)
2107 (void) putchar('+'); /* all selected */
2109 (void) jputchar('+'); /* all selected */
2113 (void) putchar('#'); /* count selected */
2115 (void) jputchar('#'); /* count selected */
2119 (void) putchar(*cp);
2121 (void) jputchar(*cp);
2124 if (n > attr_n && (color != NO_COLOR || attr != ATR_NONE))
2125 toggle_menu_attr(FALSE, color, attr);
2126 } /* if npages > 0 */
2133 /* remember how many explicit menu choices there are */
2134 resp_len = (int) strlen(resp);
2136 /* corner window - clear extra lines from last page */
2138 for (n = page_lines + 1; n < cw->maxrow; n++) {
2139 tty_curs(window, 1, n);
2144 /* set extra chars.. */
2145 Strcat(resp, default_menu_cmds);
2146 Strcat(resp, " "); /* next page or end */
2147 Strcat(resp, "0123456789\033\n\r"); /* counts, quit */
2148 Strcat(resp, gacc); /* group accelerators */
2149 Strcat(resp, mapped_menu_cmds);
2153 Sprintf(cw->morestr, "(%d of %d)", curr_page + 1,
2156 Sprintf(cw->morestr, "(%d/%d)", curr_page + 1,
2160 Strcpy(cw->morestr, msave);
2162 Strcpy(cw->morestr, defmorestr);
2164 tty_curs(window, 1, page_lines);
2168 /* just put the cursor back... */
2169 tty_curs(window, (int) strlen(cw->morestr) + 2, page_lines);
2170 xwaitforspace(resp);
2173 really_morc = morc; /* (only used with MENU_EXPLICIT_CHOICE */
2174 if ((rp = index(resp, morc)) != 0 && rp < resp + resp_len)
2175 /* explicit menu selection; don't override it if it also
2176 happens to match a mapped menu command (such as ':' to
2177 look inside a container vs ':' to search) */
2178 morc = MENU_EXPLICIT_CHOICE;
2180 morc = map_menu_cmd(morc);
2184 /* special case: '0' is also the default ball class */
2185 if (!counting && index(gacc, morc))
2187 /* fall through to count the zero */
2198 count = (count * 10L) + (long) (morc - '0');
2200 * It is debatable whether we should allow 0 to
2201 * start a count. There is no difference if the
2202 * item is selected. If not selected, then
2205 * count starting zero: "zero b's"
2206 * ignore starting zero: "select b"
2208 * At present I don't know which is better.
2210 if (count != 0L) { /* ignore leading zeros */
2212 reset_count = FALSE;
2215 case '\033': /* cancel - from counting or loop */
2217 /* deselect everything */
2218 for (curr = cw->mlist; curr; curr = curr->next) {
2219 curr->selected = FALSE;
2222 cw->flags |= WIN_CANCELLED;
2225 /* else only stop count */
2227 case '\0': /* finished (commit) */
2230 /* only finished if we are actually picking something */
2231 if (cw->how != PICK_NONE) {
2235 /* else fall through */
2237 case MENU_NEXT_PAGE:
2238 if (cw->npages > 0 && curr_page != cw->npages - 1) {
2241 } else if (morc == ' ') {
2242 /* ' ' finishes menus here, but stop '>' doing the same. */
2246 case MENU_PREVIOUS_PAGE:
2247 if (cw->npages > 0 && curr_page != 0) {
2252 case MENU_FIRST_PAGE:
2253 if (cw->npages > 0 && curr_page != 0) {
2258 case MENU_LAST_PAGE:
2259 if (cw->npages > 0 && curr_page != cw->npages - 1) {
2261 curr_page = cw->npages - 1;
2264 case MENU_SELECT_PAGE:
2265 if (cw->how == PICK_ANY)
2266 set_all_on_page(window, page_start, page_end);
2268 case MENU_UNSELECT_PAGE:
2269 unset_all_on_page(window, page_start, page_end);
2271 case MENU_INVERT_PAGE:
2272 if (cw->how == PICK_ANY)
2273 invert_all_on_page(window, page_start, page_end, 0);
2275 case MENU_SELECT_ALL:
2276 if (cw->how == PICK_ANY) {
2277 set_all_on_page(window, page_start, page_end);
2279 for (curr = cw->mlist; curr; curr = curr->next)
2280 if (curr->identifier.a_void && !curr->selected)
2281 curr->selected = TRUE;
2284 case MENU_UNSELECT_ALL:
2285 unset_all_on_page(window, page_start, page_end);
2286 /* unset the rest */
2287 for (curr = cw->mlist; curr; curr = curr->next)
2288 if (curr->identifier.a_void && curr->selected) {
2289 curr->selected = FALSE;
2293 case MENU_INVERT_ALL:
2294 if (cw->how == PICK_ANY)
2295 invert_all(window, page_start, page_end, 0);
2298 if (cw->how == PICK_NONE) {
2302 char searchbuf[BUFSZ + 2], tmpbuf[BUFSZ] = DUMMY;
2303 boolean on_curr_page = FALSE;
2306 tty_getlin("Search for:", tmpbuf);
2307 if (!tmpbuf[0] || tmpbuf[0] == '\033')
2309 Sprintf(searchbuf, "*%s*", tmpbuf);
2311 for (curr = cw->mlist; curr; curr = curr->next) {
2314 if (curr == page_start)
2315 on_curr_page = TRUE;
2316 else if (curr == page_end)
2317 on_curr_page = FALSE;
2318 if (curr->identifier.a_void
2319 && pmatchi(searchbuf, curr->str)) {
2320 toggle_menu_curr(window, curr, lineno, on_curr_page,
2322 if (cw->how == PICK_ONE) {
2330 case MENU_EXPLICIT_CHOICE:
2334 if (cw->how == PICK_NONE || !index(resp, morc)) {
2335 /* unacceptable input received */
2338 } else if (index(gacc, morc)) {
2340 /* group accelerator; for the PICK_ONE case, we know that
2341 it matches exactly one item in order to be in gacc[] */
2342 invert_all(window, page_start, page_end, morc);
2343 if (cw->how == PICK_ONE)
2347 /* find, toggle, and possibly update */
2348 for (n = 0, curr = page_start; curr != page_end;
2349 n++, curr = curr->next)
2350 if (morc == curr->selector) {
2351 toggle_menu_curr(window, curr, n, TRUE, counting, count);
2352 if (cw->how == PICK_ONE)
2354 break; /* from `for' loop */
2360 cw->morestr = msave;
2361 free((genericptr_t) morestr);
2365 process_text_window(window, cw)
2375 for (n = 0, i = 0; i < cw->maxrow; i++) {
2377 if (!cw->offx && (n + cw->offy == ttyDisplay->rows - 1)) {
2378 tty_curs(window, 1, n);
2380 dmore(cw, quitchars);
2381 if (morc == '\033') {
2382 cw->flags |= WIN_CANCELLED;
2386 tty_curs(window, 1, 0);
2392 tty_curs(window, 1, n++);
2400 attr = cw->data[i][0] - 1;
2403 (void) putchar(' ');
2405 (void) jputchar(' ');
2409 term_start_attr(attr);
2411 for (cp = &cw->data[i][1], linestart = TRUE;
2413 for (cp = &cw->data[i][1];
2416 *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
2419 *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
2420 cp++, ttyDisplay->curx++
2423 /* message recall for msg_window:full/combination/reverse
2424 might have output from '/' in it (see redotoplin()) */
2426 if (linestart && (*cp & 0x80) != 0) {
2431 (void) putchar(*cp);
2434 (void) jputchar(*cp);
2437 term_end_attr(attr);
2440 if (i == cw->maxrow) {
2442 if (cw->type == NHW_TEXT) {
2443 tty_curs(BASE_WINDOW, 1, (int) ttyDisplay->cury + 1);
2447 tty_curs(BASE_WINDOW, (int) cw->offx + 1,
2448 (cw->type == NHW_TEXT) ? (int) ttyDisplay->rows - 1 : n);
2450 dmore(cw, quitchars);
2452 cw->flags |= WIN_CANCELLED;
2458 tty_display_nhwindow(window, blocking)
2460 boolean blocking; /* with ttys, all windows are blocking */
2462 register struct WinDesc *cw = 0;
2466 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2467 panic(winpanicstr, window);
2468 if (cw->flags & WIN_CANCELLED)
2470 ttyDisplay->lastwin = window;
2471 ttyDisplay->rawprint = 0;
2473 print_vt_code2(AVTC_SELECT_WINDOW, window);
2477 if (ttyDisplay->toplin == 1) {
2479 ttyDisplay->toplin = 1; /* more resets this */
2480 tty_clear_nhwindow(window);
2482 ttyDisplay->toplin = 0;
2483 cw->curx = cw->cury = 0;
2485 iflags.window_inited = TRUE;
2490 if (!ttyDisplay->toplin)
2491 ttyDisplay->toplin = 1;
2492 tty_display_nhwindow(WIN_MESSAGE, TRUE);
2497 (void) fflush(stdout);
2500 cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
2504 /* cw->maxcol is a long, but its value is constrained to
2505 be <= ttyDisplay->cols, so is sure to fit within a short */
2506 s_maxcol = (short) cw->maxcol;
2508 cw->offx = (cw->type == NHW_TEXT)
2510 : min(min(82, ttyDisplay->cols / 2),
2511 ttyDisplay->cols - s_maxcol - 1);
2513 /* avoid converting to uchar before calculations are finished */
2514 cw->offx = (uchar) max((int) 10,
2515 (int) (ttyDisplay->cols - s_maxcol - 1));
2519 if (cw->type == NHW_MENU)
2521 if (ttyDisplay->toplin == 1)
2522 tty_display_nhwindow(WIN_MESSAGE, TRUE);
2524 if (cw->maxrow >= (int) ttyDisplay->rows
2525 || !iflags.menu_overlay)
2527 if (cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows
2528 || !iflags.menu_overlay)
2532 if (cw->offy || iflags.menu_overlay) {
2533 tty_curs(window, 1, 0);
2537 ttyDisplay->toplin = 0;
2539 if (WIN_MESSAGE != WIN_ERR)
2540 tty_clear_nhwindow(WIN_MESSAGE);
2543 if (cw->data || !cw->maxrow)
2544 process_text_window(window, cw);
2546 process_menu_window(window, cw);
2553 tty_dismiss_nhwindow(window)
2556 register struct WinDesc *cw = 0;
2559 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2560 panic(winpanicstr, window);
2562 print_vt_code2(AVTC_SELECT_WINDOW, window);
2566 if (ttyDisplay->toplin)
2567 tty_display_nhwindow(WIN_MESSAGE, TRUE);
2573 * these should only get dismissed when the game is going away
2576 tty_curs(BASE_WINDOW, 1, (int) ttyDisplay->rows - 1);
2582 if (iflags.window_inited) {
2583 /* otherwise dismissing the text endwin after other windows
2584 * are dismissed tries to redraw the map and panics. since
2585 * the whole reason for dismissing the other windows was to
2586 * leave the ending window on the screen, we don't want to
2589 erase_menu_or_text(window, cw, FALSE);
2599 tty_destroy_nhwindow(window)
2602 register struct WinDesc *cw = 0;
2604 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2605 panic(winpanicstr, window);
2608 tty_dismiss_nhwindow(window);
2609 if (cw->type == NHW_MESSAGE)
2610 iflags.window_inited = 0;
2611 if (cw->type == NHW_MAP)
2614 free_window_info(cw, TRUE);
2615 free((genericptr_t) cw);
2616 wins[window] = 0; /* available for re-use */
2620 tty_curs(window, x, y)
2622 register int x, y; /* not xchar: perhaps xchar is unsigned and
2623 curx-x would be unsigned as well */
2625 struct WinDesc *cw = 0;
2626 int cx = ttyDisplay->curx;
2627 int cy = ttyDisplay->cury;
2630 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2631 panic(winpanicstr, window);
2632 ttyDisplay->lastwin = window;
2634 print_vt_code2(AVTC_SELECT_WINDOW, window);
2636 #if defined(USE_TILES) && defined(MSDOS)
2637 adjust_cursor_flags(cw);
2639 cw->curx = --x; /* column 0 is never used */
2642 if (x < 0 || y < 0 || y >= cw->rows || x > cw->cols) {
2643 const char *s = "[unknown type]";
2647 s = "[topl window]";
2650 s = "[status window]";
2656 s = "[corner window]";
2659 s = "[text window]";
2662 s = "[base window]";
2665 debugpline4("bad curs positioning win %d %s (%d,%d)", window, s, x, y);
2666 /* This return statement caused a functional difference between
2667 DEBUG and non-DEBUG operation, so it is being commented
2668 out. It caused tty_curs() to fail to move the cursor to the
2669 location it needed to be if the x,y range checks failed,
2670 leaving the next piece of output to be displayed at whatever
2671 random location the cursor happened to be at prior. */
2680 if (clipping && window == WIN_MAP) {
2686 if (y == cy && x == cx)
2689 if (cw->type == NHW_MAP)
2693 if (!nh_ND && (cx != x || x <= 3)) { /* Extremely primitive */
2694 cmov(x, y); /* bunker!wtm */
2703 if (cy <= 3 && cx <= 3) {
2706 } else if ((x <= 3 && cy <= 3) || (!nh_CM && x < cx)) {
2708 (void) putchar('\r');
2710 (void) cputchar('\r');
2712 ttyDisplay->curx = 0;
2714 } else if (!nh_CM) {
2720 ttyDisplay->curx = x;
2721 ttyDisplay->cury = y;
2725 tty_putsym(window, x, y, ch)
2730 register struct WinDesc *cw = 0;
2733 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2734 panic(winpanicstr, window);
2736 print_vt_code2(AVTC_SELECT_WINDOW, window);
2739 #ifndef STATUS_HILITES
2744 tty_curs(window, x, y);
2748 if(cw->type!=NHW_MAP)
2749 (void) jputchar(ch);
2751 (void) cputchar(ch);
2760 impossible("Can't putsym to window type %d", cw->type);
2765 STATIC_OVL const char *
2769 static char cbuf[BUFSZ];
2771 /* compress out consecutive spaces if line is too long;
2772 topline wrapping converts space at wrap point into newline,
2773 we reverse that here */
2774 if ((int) strlen(str) >= CO || index(str, '\n')) {
2775 const char *in_str = str;
2776 char c, *outstr = cbuf, *outend = &cbuf[sizeof cbuf - 1];
2777 boolean was_space = TRUE; /* True discards all leading spaces;
2778 False would retain one if present */
2780 while ((c = *in_str++) != '\0' && outstr < outend) {
2783 if (was_space && c == ' ')
2786 was_space = (c == ' ');
2788 if ((was_space && outstr > cbuf) || outstr == outend)
2789 --outstr; /* remove trailing space or make room for terminator */
2797 tty_putstr(window, attr, str)
2802 register struct WinDesc *cw = 0;
2804 register long i, n0;
2805 #ifndef STATUS_HILITES
2806 register const char *nb;
2809 int kchar2 = 0; /* if 1, kanji 2nd byte */
2814 /* Assume there's a real problem if the window is missing --
2815 * probably a panic message
2818 jputchar('\0'); /* RESET */
2821 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) {
2826 if (str == (const char *) 0
2827 || ((cw->flags & WIN_CANCELLED) && (cw->type != NHW_MESSAGE)))
2829 if (cw->type != NHW_MESSAGE)
2830 str = compress_str(str);
2832 ttyDisplay->lastwin = window;
2834 print_vt_code2(AVTC_SELECT_WINDOW, window);
2838 int suppress_history = (attr & ATR_NOHISTORY);
2840 /* in case we ever support display attributes for topline
2841 messages, clear flag mask leaving only display attr */
2842 /*attr &= ~(ATR_URGENT | ATR_NOHISTORY);*/
2844 /* really do this later */
2845 #if defined(USER_SOUNDS) && defined(WIN32CON)
2846 play_sound_for_message(str);
2848 if (!suppress_history) {
2849 /* normal output; add to current top line if room, else flush
2850 whatever is there to history and then write this */
2853 /* put anything already on top line into history */
2855 /* write to top line without remembering what we're writing */
2860 #ifndef STATUS_HILITES
2862 ob = &cw->data[cw->cury][j = cw->curx];
2865 if (!cw->cury && (int) strlen(str) >= CO) {
2866 /* the characters before "St:" are unnecessary */
2867 nb = index(str, ':');
2868 if (nb && nb > str + 2)
2872 for (i = cw->curx + 1, n0 = cw->cols; i < n0; i++, nb++) {
2874 if (*ob || context.botlx) {
2875 /* last char printed may be in middle of line */
2876 tty_curs(WIN_STATUS, i, cw->cury);
2882 #else /* this code updates all status line at any time */
2884 /* check 2-bytes character for Japanese */
2885 /* by issei 93/12/2 */
2886 uc = *((unsigned char *)nb);
2887 if((!(uc & 0x80) && *ob != *nb) || kflg){
2888 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2891 if(*ob != *nb || *(ob+1)!= *(nb+1)){
2892 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2897 #define ismbchar(c) (((unsigned char)(c)) & 0x80)
2900 if (kchar2) /* kanji 2nd byte */
2902 if (kchar2 & KUPDATE)
2903 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2906 else if (ismbchar(*nb)) /* kanji 1st byte */
2909 /* Kanji char must be checked as 2-bytes pair. */
2910 /* check i to prevent putting only kanji 1st byte at last. */
2911 if ((*nb != *ob || *(nb+1) != *(ob+1)) && i < n0-1)
2913 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2914 kchar2 |= KUPDATE; /* must do update */
2916 /* else nb is the same char as old, so need not to update */
2918 /* not kanji char */
2922 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2927 (void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1);
2928 cw->data[cw->cury][cw->cols - 1] = '\0'; /* null terminate */
2929 cw->cury = (cw->cury + 1) % cw->maxrow;
2932 #endif /* STATUS_HILITES */
2934 tty_curs(window, cw->curx + 1, cw->cury);
2935 term_start_attr(attr);
2936 while (*str && (int) ttyDisplay->curx < (int) ttyDisplay->cols - 1) {
2938 (void) putchar(*str);
2940 (void) cputchar(*str);
2947 term_end_attr(attr);
2950 tty_curs(window, cw->curx + 1, cw->cury);
2951 term_start_attr(attr);
2953 if ((int) ttyDisplay->curx >= (int) ttyDisplay->cols - 1) {
2956 tty_curs(window, cw->curx + 1, cw->cury);
2959 (void) putchar(*str);
2961 (void) jputchar(*str);
2968 term_end_attr(attr);
2973 if (cw->type == NHW_TEXT
2974 && (cw->cury + cw->offy) == ttyDisplay->rows - 1)
2976 if (cw->type == NHW_TEXT && cw->cury == ttyDisplay->rows - 1)
2979 /* not a menu, so save memory and output 1 page at a time */
2980 cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
2981 tty_display_nhwindow(window, TRUE);
2982 for (i = 0; i < cw->maxrow; i++)
2984 free((genericptr_t) cw->data[i]);
2987 cw->maxrow = cw->cury = 0;
2989 /* always grows one at a time, but alloc 12 at a time */
2990 if (cw->cury >= cw->rows) {
2994 tmp = (char **) alloc(sizeof(char *) * (unsigned) cw->rows);
2995 for (i = 0; i < cw->maxrow; i++)
2996 tmp[i] = cw->data[i];
2998 free((genericptr_t) cw->data);
3001 for (i = cw->maxrow; i < cw->rows; i++)
3004 if (cw->data[cw->cury])
3005 free((genericptr_t) cw->data[cw->cury]);
3006 n0 = (long) strlen(str) + 1L;
3007 ob = cw->data[cw->cury] = (char *) alloc((unsigned) n0 + 1);
3008 *ob++ = (char) (attr + 1); /* avoid nuls, for convenience */
3011 if (n0 > cw->maxcol)
3013 if (++cw->cury > cw->maxrow)
3014 cw->maxrow = cw->cury;
3016 /* attempt to break the line */
3017 for (i = CO - 1; i && str[i] != ' ' && str[i] != '\n';)
3020 cw->data[cw->cury - 1][++i] = '\0';
3021 tty_putstr(window, attr, &str[i]);
3027 jputchar('\0'); /* RESET */
3032 tty_display_file(fname, complain)
3036 #ifdef DEF_PAGER /* this implies that UNIX is defined */
3038 /* use external pager; this may give security problems */
3039 register int fd = open(fname, 0);
3043 pline("Cannot open %s.", fname);
3049 /* Now that child() does a setuid(getuid()) and a chdir(),
3050 we may not be able to open file fname anymore, so make
3055 raw_printf("Cannot open %s as stdin.", fname);
3057 (void) execlp(catmore, "page", (char *) 0);
3059 raw_printf("Cannot exec %s.", catmore);
3062 sleep(10); /* want to wait_synch() but stdin is gone */
3063 nh_terminate(EXIT_FAILURE);
3070 #else /* DEF_PAGER */
3076 tty_clear_nhwindow(WIN_MESSAGE);
3077 f = dlb_fopen(fname, "r");
3085 pline("Cannot open \"%s\".", fname);
3089 winid datawin = tty_create_nhwindow(NHW_TEXT);
3090 boolean empty = TRUE;
3097 /* attempt to scroll text below map window if there's room */
3098 wins[datawin]->offy = wins[WIN_STATUS]->offy + 3;
3099 if ((int) wins[datawin]->offy + 12 > (int) ttyDisplay->rows)
3100 wins[datawin]->offy = 0;
3102 while (dlb_fgets(buf, BUFSZ, f)) {
3103 if ((cr = index(buf, '\n')) != 0)
3106 if ((cr = index(buf, '\r')) != 0)
3109 if (index(buf, '\t') != 0)
3110 (void) tabexpand(buf);
3112 tty_putstr(datawin, 0, buf);
3113 if (wins[datawin]->flags & WIN_CANCELLED)
3117 tty_display_nhwindow(datawin, FALSE);
3118 tty_destroy_nhwindow(datawin);
3119 (void) dlb_fclose(f);
3122 #endif /* DEF_PAGER */
3126 tty_start_menu(window)
3129 tty_clear_nhwindow(window);
3135 * Add a menu item to the beginning of the menu list. This list is reversed
3139 tty_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
3140 winid window; /* window to use, must be of type NHW_MENU */
3141 int glyph UNUSED; /* glyph to display with item (not used) */
3142 const anything *identifier; /* what to return if selected */
3143 char ch; /* keyboard accelerator (0 = pick our own) */
3144 char gch; /* group accelerator (0 = no group) */
3145 int attr; /* attribute for string (like tty_putstr()) */
3146 const char *str; /* menu string */
3147 boolean preselected; /* item is marked as selected */
3149 register struct WinDesc *cw = 0;
3150 tty_menu_item *item;
3152 char buf[4 + BUFSZ];
3155 if (str == (const char *) 0)
3158 if (window == WIN_ERR
3159 || (cw = wins[window]) == (struct WinDesc *) 0
3160 || cw->type != NHW_MENU)
3161 panic(winpanicstr, window);
3164 if (identifier->a_void) {
3165 int len = (int) strlen(str);
3168 /* We *think* everything's coming in off at most BUFSZ bufs... */
3169 impossible("Menu item too long (%d).", len);
3172 Sprintf(buf, "%c - ", ch ? ch : '?');
3173 (void) strncpy(buf + 4, str, len);
3174 buf[4 + len] = '\0';
3179 item = (tty_menu_item *) alloc(sizeof *item);
3180 item->identifier = *identifier;
3182 item->selected = preselected;
3183 item->selector = ch;
3184 item->gselector = gch;
3186 item->str = dupstr(newstr ? newstr : "");
3188 item->next = cw->mlist;
3192 /* Invert the given list, can handle NULL as an input. */
3193 STATIC_OVL tty_menu_item *
3195 tty_menu_item *curr;
3197 tty_menu_item *next, *head = 0;
3209 * End a menu in this window, window must a type NHW_MENU. This routine
3210 * processes the string list. We calculate the # of pages, then assign
3211 * keyboard accelerators as needed. Finally we decide on the width and
3212 * height of the window.
3215 tty_end_menu(window, prompt)
3216 winid window; /* menu to use */
3217 const char *prompt; /* prompt to for menu */
3219 struct WinDesc *cw = 0;
3220 tty_menu_item *curr;
3225 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
3226 || cw->type != NHW_MENU)
3227 panic(winpanicstr, window);
3229 /* Reverse the list so that items are in correct order. */
3230 cw->mlist = reverse(cw->mlist);
3232 /* Put the prompt at the beginning of the menu. */
3236 any = zeroany; /* not selectable */
3237 tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
3239 tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt,
3243 /* 52: 'a'..'z' and 'A'..'Z'; avoids selector duplication within a page */
3244 lmax = min(52, (int) ttyDisplay->rows - 1); /* # lines per page */
3245 cw->npages = (cw->nitems + (lmax - 1)) / lmax; /* # of pages */
3248 * For really tall page, allow 53 if '$' or '#' is present and
3249 * 54 if both are. [Only for single page menu, otherwise pages
3250 * without those could try to use too many letters.]
3251 * Probably not worth bothering with; anyone with a display big
3252 * for this to matter will likely switch from tty to curses for
3253 * multi-line message window and/or persistent inventory window.
3256 /* make sure page list is large enough */
3257 if (cw->plist_size < cw->npages + 1) { /* +1: need one slot beyond last */
3259 free((genericptr_t) cw->plist);
3260 cw->plist_size = cw->npages + 1;
3261 cw->plist = (tty_menu_item **) alloc(cw->plist_size
3262 * sizeof (tty_menu_item *));
3265 cw->cols = 0; /* cols is set when the win is initialized... (why?) */
3266 menu_ch = '?'; /* lint suppression */
3267 for (n = 0, curr = cw->mlist; curr; n++, curr = curr->next) {
3268 /* set page boundaries and character accelerators */
3269 if ((n % lmax) == 0) {
3271 cw->plist[n / lmax] = curr;
3273 if (curr->identifier.a_void && !curr->selector) {
3274 curr->str[0] = curr->selector = menu_ch;
3275 if (menu_ch++ == 'z')
3279 /* cut off any lines that are too long */
3280 len = strlen(curr->str) + 2; /* extra space at beg & end */
3281 if (len > (int) ttyDisplay->cols) {
3282 curr->str[ttyDisplay->cols - 2] = 0;
3283 len = ttyDisplay->cols;
3288 cw->plist[cw->npages] = 0; /* plist terminator */
3291 * If greater than 1 page, morestr is "(x of y) " otherwise, "(end) "
3293 if (cw->npages > 1) {
3295 /* produce the largest demo string */
3296 Sprintf(buf, "(%ld of %ld) ", cw->npages, cw->npages);
3298 cw->morestr = dupstr("");
3300 cw->morestr = dupstr("(end) ");
3301 len = strlen(cw->morestr);
3304 if (len > (int) ttyDisplay->cols) {
3305 /* truncate the prompt if it's too long for the screen */
3306 if (cw->npages <= 1) /* only str in single page case */
3307 cw->morestr[ttyDisplay->cols] = 0;
3308 len = ttyDisplay->cols;
3313 cw->maxcol = cw->cols;
3316 * The number of lines in the first page plus the morestr will be the
3317 * maximum size of the window.
3320 cw->maxrow = cw->rows = lmax + 1;
3322 cw->maxrow = cw->rows = cw->nitems + 1;
3326 tty_select_menu(window, how, menu_list)
3329 menu_item **menu_list;
3331 register struct WinDesc *cw = 0;
3332 tty_menu_item *curr;
3336 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
3337 || cw->type != NHW_MENU)
3338 panic(winpanicstr, window);
3340 *menu_list = (menu_item *) 0;
3341 cw->how = (short) how;
3343 tty_display_nhwindow(window, TRUE);
3344 cancelled = !!(cw->flags & WIN_CANCELLED);
3345 tty_dismiss_nhwindow(window); /* does not destroy window data */
3350 for (n = 0, curr = cw->mlist; curr; curr = curr->next)
3356 *menu_list = (menu_item *) alloc(n * sizeof(menu_item));
3357 for (mi = *menu_list, curr = cw->mlist; curr; curr = curr->next)
3358 if (curr->selected) {
3359 mi->item = curr->identifier;
3360 mi->count = curr->count;
3368 /* special hack for treating top line --More-- as a one item menu */
3370 tty_message_menu(let, how, mesg)
3376 /* "menu" without selection; use ordinary pline, no more() */
3377 if (how == PICK_NONE) {
3382 ttyDisplay->dismiss_more = let;
3384 /* barebones pline(); since we're only supposed to be called after
3385 response to a prompt, we'll assume that the display is up to date */
3386 tty_putstr(WIN_MESSAGE, 0, mesg);
3387 /* if `mesg' didn't wrap (triggering --More--), force --More-- now */
3388 if (ttyDisplay->toplin == 1) {
3390 ttyDisplay->toplin = 1; /* more resets this */
3391 tty_clear_nhwindow(WIN_MESSAGE);
3393 /* normally <ESC> means skip further messages, but in this case
3394 it means cancel the current prompt; any other messages should
3395 continue to be output normally */
3396 wins[WIN_MESSAGE]->flags &= ~WIN_CANCELLED;
3397 ttyDisplay->dismiss_more = 0;
3399 return ((how == PICK_ONE && morc == let) || morc == '\033') ? morc : '\0';
3403 tty_update_inventory()
3412 (void) fflush(stdout);
3419 /* we just need to make sure all windows are synch'd */
3420 if (!ttyDisplay || ttyDisplay->rawprint) {
3423 ttyDisplay->rawprint = 0;
3425 tty_display_nhwindow(WIN_MAP, FALSE);
3426 if (ttyDisplay->inmore) {
3427 addtopl("--More--");
3428 (void) fflush(stdout);
3429 } else if (ttyDisplay->inread > program_state.gameover) {
3430 /* this can only happen if we were reading and got interrupted */
3431 ttyDisplay->toplin = 3;
3432 /* do this twice; 1st time gets the Quit? message again */
3433 (void) tty_doprev_message();
3434 (void) tty_doprev_message();
3436 (void) fflush(stdout);
3442 docorner(xmin, ymax)
3443 register int xmin, ymax;
3446 register struct WinDesc *cw = wins[WIN_MAP];
3449 #if 0 /* this optimization is not valuable enough to justify
3450 abusing core internals... */
3451 if (u.uswallow) { /* Can be done more efficiently */
3453 /* without this flush, if we happen to follow --More-- displayed in
3454 leftmost column, the cursor gets left in the wrong place after
3455 <docorner<more<update_topl<tty_putstr calls unwind back to core */
3461 #if defined(SIGWINCH) && defined(CLIPPING)
3463 ymax = LI; /* can happen if window gets smaller */
3465 for (y = 0; y < ymax; y++) {
3466 tty_curs(BASE_WINDOW, xmin, y); /* move cursor */
3467 cl_end(); /* clear to end of line */
3469 if (y < (int) cw->offy || y + clipy > ROWNO)
3470 continue; /* only refresh board */
3471 #if defined(USE_TILES) && defined(MSDOS)
3472 if (iflags.tile_view)
3473 row_refresh((xmin / 2) + clipx - ((int) cw->offx / 2), COLNO - 1,
3474 y + clipy - (int) cw->offy);
3477 row_refresh(xmin + clipx - (int) cw->offx, COLNO - 1,
3478 y + clipy - (int) cw->offy);
3480 if (y < cw->offy || y > ROWNO)
3481 continue; /* only refresh board */
3482 row_refresh(xmin - (int) cw->offx, COLNO - 1, y - (int) cw->offy);
3487 if (ymax >= (int) wins[WIN_STATUS]->offy) {
3488 /* we have wrecked the bottom line */
3498 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3505 if (ttyDisplay->color != NO_COLOR) {
3507 ttyDisplay->color = NO_COLOR;
3517 register char ch = (char) in_ch;
3520 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3521 if (SYMHANDLING(H_IBM)
3522 /* for DECgraphics, lower-case letters with high bit set mean
3523 switch character set and render with high bit clear;
3524 user might want 8-bits for other characters */
3525 || (iflags.eight_bit_tty && (!SYMHANDLING(H_DEC)
3526 || (in_ch & 0x7f) < 0x60))) {
3527 /* IBM-compatible displays don't need other stuff */
3531 (void) cputchar(ch);
3533 } else if (ch & 0x80) {
3534 if (!GFlag || HE_resets_AS) {
3539 (void) putchar((ch ^ 0x80)); /* Strip 8th bit */
3541 (void) cputchar((ch ^ 0x80)); /* Strip 8th bit */
3551 (void) jputchar(ch);
3558 #endif /* ASCIIGRAPH && !NO_TERMS */
3571 clipymax = LI - 1 - iflags.wc2_statuslines;
3575 tty_cliparound(x, y)
3578 int oldx = clipx, oldy = clipy;
3583 if (x < clipx + 5) {
3584 clipx = max(0, x - 20);
3585 clipxmax = clipx + CO;
3586 } else if (x > clipxmax - 5) {
3587 clipxmax = min(COLNO, clipxmax + 20);
3588 clipx = clipxmax - CO;
3590 if (y < clipy + 2) {
3591 clipy = max(0, y - (clipymax - clipy) / 2);
3592 clipymax = clipy + (LI - 1 - iflags.wc2_statuslines);
3593 } else if (y > clipymax - 2) {
3594 clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2);
3595 clipy = clipymax - (LI - 1 - iflags.wc2_statuslines);
3597 if (clipx != oldx || clipy != oldy) {
3598 redraw_map(); /* ask the core to resend the map window's data */
3601 #endif /* CLIPPING */
3606 * Print the glyph to the output device. Don't flush the output device.
3608 * Since this is only called from show_glyph(), it is assumed that the
3609 * position and glyph are always correct (checked there)!
3613 tty_print_glyph(window, x, y, glyph, bkglyph)
3620 boolean reverse_on = FALSE;
3627 if (x <= clipx || y < clipy || x >= clipxmax || y >= clipymax)
3631 /* map glyph to character and color */
3632 (void) mapglyph(glyph, &ch, &color, &special, x, y, 0);
3634 print_vt_code2(AVTC_SELECT_WINDOW, window);
3636 /* Move the cursor. */
3637 tty_curs(window, x, y);
3639 print_vt_code3(AVTC_GLYPH_START, glyph2tile[glyph], special);
3642 if (ul_hack && ch == '_') { /* non-destructive underscore */
3644 (void) putchar((char) ' ');
3646 (void) cputchar((char) ' ');
3653 if (color != ttyDisplay->color) {
3654 if (ttyDisplay->color != NO_COLOR)
3656 ttyDisplay->color = color;
3657 if (color != NO_COLOR)
3658 term_start_color(color);
3660 #endif /* TEXTCOLOR */
3662 /* must be after color check; term_end_color may turn off inverse too */
3663 if (((special & MG_PET) && iflags.hilite_pet)
3664 || ((special & MG_OBJPILE) && iflags.hilite_pile)
3665 || ((special & MG_DETECT) && iflags.use_inverse)
3666 || ((special & MG_BW_LAVA) && iflags.use_inverse)) {
3667 term_start_attr(ATR_INVERSE);
3671 #if defined(USE_TILES) && defined(MSDOS)
3672 if (iflags.grmode && iflags.tile_view)
3673 xputg(glyph, ch, special);
3676 g_putch(ch); /* print the character */
3679 term_end_attr(ATR_INVERSE);
3681 /* turn off color as well, ATR_INVERSE may have done this already */
3682 if (ttyDisplay->color != NO_COLOR) {
3684 ttyDisplay->color = NO_COLOR;
3689 print_vt_code1(AVTC_GLYPH_END);
3691 wins[window]->curx++; /* one character over */
3692 ttyDisplay->curx++; /* the real cursor moved too */
3701 ttyDisplay->rawprint++;
3702 print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
3703 #if defined(MICRO) || defined(WIN32CON)
3711 (void) fflush(stdout);
3716 tty_raw_print_bold(str)
3721 ttyDisplay->rawprint++;
3722 print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
3723 term_start_raw_bold();
3724 #if defined(MICRO) || defined(WIN32CON)
3728 (void) fputs(str, stdout);
3733 term_end_raw_bold();
3734 #if defined(MICRO) || defined(WIN32CON)
3740 (void) fflush(stdout);
3749 /* kludge alert: Some Unix variants return funny values if getc()
3750 * is called, interrupted, and then called again. There
3751 * is non-reentrant code in the internal _filbuf() routine, called by
3754 static volatile int nesting = 0;
3758 HUPSKIP_RESULT('\033');
3759 print_vt_code1(AVTC_INLINE_SYNC);
3760 (void) fflush(stdout);
3761 /* Note: if raw_print() and wait_synch() get called to report terminal
3762 * initialization problems, then wins[] and ttyDisplay might not be
3763 * available yet. Such problems will probably be fatal before we get
3764 * here, but validate those pointers just in case...
3766 if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
3767 wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
3768 if (iflags.debug_fuzzer) {
3772 i = (++nesting == 1)
3774 : (read(fileno(stdin), (genericptr_t) &nestbuf, 1) == 1)
3775 ? (int) nestbuf : EOF;
3782 i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
3784 i = '\033'; /* same for EOF */
3785 if (ttyDisplay && ttyDisplay->toplin == 1)
3786 ttyDisplay->toplin = 2;
3787 #ifdef TTY_TILES_ESCCODES
3789 /* hack to force output of the window select code */
3790 int tmp = vt_tile_current_window;
3792 vt_tile_current_window++;
3793 print_vt_code2(AVTC_SELECT_WINDOW, tmp);
3795 #endif /* TTY_TILES_ESCCODES */
3800 * return a key, or 0, in which case a mouse button was pressed
3801 * mouse events should be returned as character postitions in the map window.
3802 * Since normal tty's don't have mice, just return a key.
3806 tty_nh_poskey(x, y, mod)
3811 HUPSKIP_RESULT('\033');
3812 #if defined(WIN32CON)
3813 (void) fflush(stdout);
3814 /* Note: if raw_print() and wait_synch() get called to report terminal
3815 * initialization problems, then wins[] and ttyDisplay might not be
3816 * available yet. Such problems will probably be fatal before we get
3817 * here, but validate those pointers just in case...
3819 if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
3820 wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
3821 i = ntposkey(x, y, mod);
3822 if (!i && mod && (*mod == 0 || *mod == EOF))
3823 i = '\033'; /* map NUL or EOF to ESC, nethack doesn't expect either */
3824 if (ttyDisplay && ttyDisplay->toplin == 1)
3825 ttyDisplay->toplin = 2;
3826 #else /* !WIN32CON */
3832 #endif /* ?WIN32CON */
3847 tty_update_positionbar(posbar)
3851 video_update_positionbar(posbar);
3854 #endif /* POSITIONBAR */
3858 * +------------------+
3860 * +------------------+
3864 * ----------------------------------------------------------------
3867 * Called from the NetHack core and receives info hand-offs
3868 * from the core, processes them, storing some information
3869 * for rendering to the tty.
3870 * -> make_things_fit()
3871 * -> render_status()
3875 * Called from tty_status_update(). It calls check_fields()
3876 * (described next) to get the required number of columns
3877 * and tries a few different ways to squish a representation
3878 * of the status window values onto the 80 column tty display.
3880 * ->set_condition_length() - update the width of conditions
3881 * ->shrink_enc() - shrink encumbrance message word
3882 * ->shrink_dlvl() - reduce the width of Dlvl:42
3886 * Verifies that all the fields are ready for display, and
3887 * returns FALSE if called too early in the startup
3888 * processing sequence. It also figures out where everything
3889 * needs to go, taking the current shrinking attempts into
3890 * account. It returns number of columns needed back to
3891 * make_things_fit(), so make_things_fit() can make attempt
3892 * to make adjustments.
3896 * Goes through each of the status row's fields and
3897 * calls tty_putstatusfield() to place them on the display.
3898 * ->tty_putstatusfield()
3899 * At the end of the for-loop, the NOW values get copied
3902 * tty_putstatusfield()
3904 * Move the cursor to the target spot, and output the field
3905 * asked for by render_status().
3907 * ----------------------------------------------------------------
3911 * The following data structures come from the genl_ routines in
3912 * src/windows.c and as such are considered to be on the window-port
3913 * "side" of things, rather than the NetHack-core "side" of things.
3915 extern const char *status_fieldfmt[MAXBLSTATS];
3916 extern char *status_vals[MAXBLSTATS];
3917 extern boolean status_activefields[MAXBLSTATS];
3918 extern winid WIN_STATUS;
3920 #ifdef STATUS_HILITES
3922 STATIC_DCL int FDECL(condcolor, (long, unsigned long *));
3924 STATIC_DCL int FDECL(condattr, (long, unsigned long *));
3925 static unsigned long *tty_colormasks;
3926 static long tty_condition_bits;
3927 static struct tty_status_fields tty_status[2][MAXBLSTATS]; /* 2: NOW,BEFORE */
3928 static int hpbar_percent, hpbar_color;
3929 static struct condition_t {
3931 const char *text[3]; /* 3: potential display vals, progressively shorter */
3933 /* The sequence order of these matters */
3934 { BL_MASK_STONE, { "Stone", "Ston", "Sto" } },
3935 { BL_MASK_SLIME, { "Slime", "Slim", "Slm" } },
3936 { BL_MASK_STRNGL, { "Strngl", "Stngl", "Str" } },
3937 { BL_MASK_FOODPOIS, { "FoodPois", "Fpois", "Poi" } },
3938 { BL_MASK_TERMILL, { "TermIll" , "Ill", "Ill" } },
3939 { BL_MASK_BLIND, { "Blind", "Blnd", "Bl" } },
3940 { BL_MASK_DEAF, { "Deaf", "Def", "Df" } },
3941 { BL_MASK_STUN, { "Stun", "Stun", "St" } },
3942 { BL_MASK_CONF, { "Conf", "Cnf", "Cf" } },
3943 { BL_MASK_HALLU, { "Hallu", "Hal", "Hl" } },
3944 { BL_MASK_LEV, { "Lev", "Lev", "Lv" } },
3945 { BL_MASK_FLY, { "Fly", "Fly", "Fl" } },
3946 { BL_MASK_RIDE, { "Ride", "Rid", "Rd" } },
3948 static const char *encvals[3][6] = {
3949 { "", "Burdened", "Stressed", "Strained", "Overtaxed", "Overloaded" },
3950 { "", "Burden", "Stress", "Strain", "Overtax", "Overload" },
3951 { "", "Brd", "Strs", "Strn", "Ovtx", "Ovld" }
3953 #define blPAD BL_FLUSH
3954 #define MAX_PER_ROW 15
3955 /* 2 or 3 status lines */
3956 static const enum statusfields
3957 twolineorder[3][MAX_PER_ROW] = {
3958 { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN,
3959 BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD },
3960 { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
3961 BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER,
3962 BL_CAP, BL_CONDITION, BL_FLUSH },
3963 /* third row of array isn't used for twolineorder */
3964 { BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD,
3965 blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }
3967 /* Align moved from 1 to 2, Leveldesc+Time+Condition moved from 2 to 3 */
3968 threelineorder[3][MAX_PER_ROW] = {
3969 { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3970 BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD },
3971 { BL_ALIGN, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
3972 BL_AC, BL_XP, BL_EXP, BL_HD, BL_HUNGER,
3973 BL_CAP, BL_FLUSH, blPAD, blPAD },
3974 { BL_LEVELDESC, BL_TIME, BL_CONDITION, BL_FLUSH, blPAD, blPAD,
3975 blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }
3977 static const enum statusfields (*fieldorder)[MAX_PER_ROW];
3979 static int finalx[3][2]; /* [rows][NOW or BEFORE] */
3980 static boolean windowdata_init = FALSE;
3981 static int cond_shrinklvl = 0;
3982 static int enclev = 0, enc_shrinklvl = 0;
3983 static int dlvl_shrinklvl = 0;
3984 static boolean truncation_expected = FALSE;
3985 #define FORCE_RESET TRUE
3986 #define NO_RESET FALSE
3988 /* This controls whether to skip fields that aren't
3989 * flagged as requiring updating during the current
3992 * Hopefully that can be confirmed as working correctly
3993 * for all platforms eventually and the conditional
3994 * setting below can be removed.
3996 static int do_field_opt =
3997 #if defined(DISABLE_TTY_FIELD_OPT)
4003 #endif /* STATUS_HILITES */
4007 * -- initialize the tty-specific data structures.
4008 * -- call genl_status_init() to initialize the general data.
4013 #ifdef STATUS_HILITES
4016 num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
4017 fieldorder = (num_rows != 3) ? twolineorder : threelineorder;
4019 for (i = 0; i < MAXBLSTATS; ++i) {
4020 tty_status[NOW][i].idx = BL_FLUSH;
4021 tty_status[NOW][i].color = NO_COLOR; /* no color */
4022 tty_status[NOW][i].attr = ATR_NONE;
4023 tty_status[NOW][i].x = tty_status[NOW][i].y = 0;
4024 tty_status[NOW][i].valid = FALSE;
4025 tty_status[NOW][i].dirty = FALSE;
4026 tty_status[NOW][i].redraw = FALSE;
4027 tty_status[NOW][i].sanitycheck = FALSE;
4028 tty_status[BEFORE][i] = tty_status[NOW][i];
4030 tty_condition_bits = 0L;
4031 hpbar_percent = 0, hpbar_color = NO_COLOR;
4032 #endif /* STATUS_HILITES */
4034 /* let genl_status_init do most of the initialization */
4039 tty_status_enablefield(fieldidx, nm, fmt, enable)
4045 genl_status_enablefield(fieldidx, nm, fmt, enable);
4048 #ifdef STATUS_HILITES
4052 * -- update the value of a status field.
4053 * -- the fldindex identifies which field is changing and
4054 * is an integer index value from botl.h
4055 * -- fldindex could be any one of the following from botl.h:
4056 * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
4057 * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
4058 * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
4059 * BL_LEVELDESC, BL_EXP, BL_CONDITION
4060 * -- fldindex could also be BL_FLUSH (-1), which is not really
4061 * a field index, but is a special trigger to tell the
4062 * windowport that it should output all changes received
4063 * to this point. It marks the end of a bot() cycle.
4064 * -- fldindex could also be BL_RESET (-3), which is not really
4065 * a field index, but is a special advisory to to tell the
4066 * windowport that it should redisplay all its status fields,
4067 * even if no changes have been presented to it.
4068 * -- ptr is usually a "char *", unless fldindex is BL_CONDITION.
4069 * If fldindex is BL_CONDITION, then ptr is a long value with
4070 * any or none of the following bits set (from botl.h):
4071 * BL_MASK_STONE 0x00000001L
4072 * BL_MASK_SLIME 0x00000002L
4073 * BL_MASK_STRNGL 0x00000004L
4074 * BL_MASK_FOODPOIS 0x00000008L
4075 * BL_MASK_TERMILL 0x00000010L
4076 * BL_MASK_BLIND 0x00000020L
4077 * BL_MASK_DEAF 0x00000040L
4078 * BL_MASK_STUN 0x00000080L
4079 * BL_MASK_CONF 0x00000100L
4080 * BL_MASK_HALLU 0x00000200L
4081 * BL_MASK_LEV 0x00000400L
4082 * BL_MASK_FLY 0x00000800L
4083 * BL_MASK_RIDE 0x00001000L
4084 * -- The value passed for BL_GOLD usually includes an encoded leading
4085 * symbol for GOLD "\GXXXXNNNN:nnn". If the window port needs to use
4086 * the textual gold amount without the leading "$:" the port will
4087 * have to skip past ':' in the passed "ptr" for the BL_GOLD case.
4088 * -- color is an unsigned int.
4089 * color_index = color & 0x00FF; CLR_* value
4090 * attribute = (color >> 8) & 0x00FF; HL_ATTCLR_* mask
4091 * This holds the color and attribute that the field should
4093 * This is relevant for everything except BL_CONDITION fldindex.
4094 * If fldindex is BL_CONDITION, this parameter should be ignored,
4095 * as condition highlighting is done via the next colormasks
4096 * parameter instead.
4098 * -- colormasks - pointer to cond_hilites[] array of colormasks.
4099 * Only relevant for BL_CONDITION fldindex. The window port
4100 * should ignore this parameter for other fldindex values.
4101 * Each condition bit must only ever appear in one of the
4102 * CLR_ array members, but can appear in multiple HL_ATTCLR_
4103 * offsets (because more than one attribute can co-exist).
4104 * See doc/window.doc for more details.
4108 tty_status_update(fldidx, ptr, chg, percent, color, colormasks)
4109 int fldidx, chg UNUSED, percent, color;
4111 unsigned long *colormasks;
4114 long *condptr = (long *) ptr;
4115 char *text = (char *) ptr;
4116 char goldbuf[40], *lastchar, *p;
4118 boolean reset_state = NO_RESET;
4120 if ((fldidx < BL_RESET) || (fldidx >= MAXBLSTATS))
4123 if ((fldidx >= 0 && fldidx < MAXBLSTATS) && !status_activefields[fldidx])
4128 reset_state = FORCE_RESET;
4131 if (make_things_fit(reset_state) || truncation_expected) {
4133 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
4134 status_sanity_check();
4139 tty_status[NOW][fldidx].idx = fldidx;
4140 tty_condition_bits = *condptr;
4141 tty_colormasks = colormasks;
4142 tty_status[NOW][fldidx].valid = TRUE;
4143 tty_status[NOW][fldidx].dirty = TRUE;
4144 tty_status[NOW][fldidx].sanitycheck = TRUE;
4145 truncation_expected = FALSE;
4148 text = decode_mixed(goldbuf, text);
4151 attrmask = (color >> 8) & 0x00FF;
4155 fmt = status_fieldfmt[fldidx];
4158 /* should be checking for first enabled field here rather than
4159 just first field, but 'fieldorder' doesn't start any rows
4160 with fields which can be disabled so [any_row][0] suffices */
4161 if (*fmt == ' ' && (fldidx == fieldorder[0][0]
4162 || fldidx == fieldorder[1][0]
4163 || fldidx == fieldorder[2][0]))
4164 ++fmt; /* skip leading space for first field on line */
4165 Sprintf(status_vals[fldidx], fmt, text);
4166 tty_status[NOW][fldidx].idx = fldidx;
4167 tty_status[NOW][fldidx].color = (color & 0x00FF);
4168 tty_status[NOW][fldidx].attr = term_attr_fixup(attrmask);
4169 tty_status[NOW][fldidx].lth = strlen(status_vals[fldidx]);
4170 tty_status[NOW][fldidx].valid = TRUE;
4171 tty_status[NOW][fldidx].dirty = TRUE;
4172 tty_status[NOW][fldidx].sanitycheck = TRUE;
4176 /* The core botl engine sends a single blank to the window port
4177 for carrying-capacity when its unused. Let's suppress that */
4178 if (fldidx >= 0 && fldidx < MAXBLSTATS
4179 && tty_status[NOW][fldidx].lth == 1
4180 && status_vals[fldidx][0] == ' ') {
4181 status_vals[fldidx][0] = '\0';
4182 tty_status[NOW][fldidx].lth = 0;
4185 /* default processing above was required before these */
4188 if (iflags.wc2_hitpointbar) {
4189 /* Special additional processing for hitpointbar */
4190 hpbar_percent = percent;
4191 hpbar_color = (color & 0x00FF);
4192 tty_status[NOW][BL_TITLE].color = hpbar_color;
4193 tty_status[NOW][BL_TITLE].dirty = TRUE;
4197 dlvl_shrinklvl = 0; /* caller is passing full length string */
4200 /* The core sends trailing blanks for some fields.
4201 Let's suppress the trailing blanks */
4202 if (tty_status[NOW][fldidx].lth > 0) {
4203 p = status_vals[fldidx];
4204 for (lastchar = eos(p); lastchar > p && *--lastchar == ' '; ) {
4206 tty_status[NOW][fldidx].lth--;
4211 /* when hitpointbar is enabled, rendering will enforce a length
4212 of 30 on title, padding with spaces or truncating if necessary */
4213 if (iflags.wc2_hitpointbar)
4214 tty_status[NOW][fldidx].lth = 30 + 2; /* '[' and ']' */
4217 /* \GXXXXNNNN counts as 1 [moot since we use decode_mixed() above] */
4218 if ((p = index(status_vals[fldidx], '\\')) != 0 && p[1] == 'G')
4219 tty_status[NOW][fldidx].lth -= (10 - 1);
4222 enc_shrinklvl = 0; /* caller is passing full length string */
4223 enclev = stat_cap_indx();
4226 /* As of 3.6.2 we only render on BL_FLUSH (or BL_RESET) */
4231 make_things_fit(force_update)
4232 boolean force_update;
4234 int trycnt, fitting = 0, requirement;
4235 int rowsz[3], num_rows, condrow, otheroptions = 0;
4237 num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
4238 condrow = num_rows - 1; /* always last row, 1 for 0..1 or 2 for 0..2 */
4240 if (enc_shrinklvl > 0 && num_rows == 2)
4242 if (dlvl_shrinklvl > 0)
4244 set_condition_length();
4245 for (trycnt = 0; trycnt < 6 && !fitting; ++trycnt) {
4246 /* FIXME: this remeasures each line every time even though it
4247 is only attempting to shrink one of them and the other one
4248 (or two) remains the same */
4249 if (!check_fields(force_update, rowsz)) {
4254 requirement = rowsz[condrow] - 1;
4255 if (requirement <= wins[WIN_STATUS]->cols - 1) {
4256 fitting = requirement;
4257 break; /* we're good */
4260 if (cond_shrinklvl < trycnt + 1) {
4261 cond_shrinklvl = trycnt + 1;
4262 set_condition_length();
4266 if (cond_shrinklvl >= 2) {
4267 /* We've exhausted the condition identifiers shrinkage,
4268 * so let's try shrinking other things...
4270 if (otheroptions < 2) {
4271 /* try shrinking the encumbrance word, but
4272 only when it's on the same line as conditions */
4274 shrink_enc(otheroptions + 1);
4275 } else if (otheroptions == 2) {
4278 /* Last resort - turn on trunction */
4279 truncation_expected = TRUE;
4289 * This is the routine where we figure out where each field
4290 * should be placed, and flag whether the on-screen details
4291 * must be updated because they need to change.
4292 * This is now done at an individual field case-by-case level.
4295 check_fields(forcefields, sz)
4296 boolean forcefields;
4299 int c, i, row, col, num_rows, idx;
4300 boolean valid = TRUE, matchprev, update_right;
4302 if (!windowdata_init && !check_windowdata())
4305 num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
4307 for (row = 0; row < num_rows; ++row) {
4310 update_right = FALSE;
4311 for (i = 0; (idx = fieldorder[row][i]) != BL_FLUSH; ++i) {
4312 if (!status_activefields[idx])
4314 if (!tty_status[NOW][idx].valid)
4316 /* might be called more than once for shrink tests, so need
4317 to reset these (redraw and x at any rate) each time */
4318 tty_status[NOW][idx].redraw = FALSE;
4319 tty_status[NOW][idx].y = row;
4320 tty_status[NOW][idx].x = col;
4322 /* On a change to the field location, everything further
4323 to the right must be updated as well. (Not necessarily
4324 everything; it's possible for complementary changes across
4325 multiple fields to put stuff further right back in sync.) */
4326 if (tty_status[NOW][idx].x + tty_status[NOW][idx].lth
4327 != tty_status[BEFORE][idx].x + tty_status[BEFORE][idx].lth)
4328 update_right = TRUE;
4329 else if (tty_status[NOW][idx].lth != tty_status[BEFORE][idx].lth
4330 || tty_status[NOW][idx].x != tty_status[BEFORE][idx].x)
4331 tty_status[NOW][idx].redraw = TRUE;
4332 else /* in case update_right is set, we're back in sync now */
4333 update_right = FALSE;
4335 matchprev = FALSE; /* assume failure */
4336 if (valid && !update_right && !forcefields
4337 && !tty_status[NOW][idx].redraw) {
4339 * Check values against those already on the display.
4340 * - Is the additional processing time for this worth it?
4343 /* color/attr checks aren't right for 'condition'
4344 and neither is examining status_vals[BL_CONDITION]
4345 so skip same-contents optimization for conditions */
4346 && idx != BL_CONDITION
4347 && (tty_status[NOW][idx].color
4348 == tty_status[BEFORE][idx].color)
4349 && (tty_status[NOW][idx].attr
4350 == tty_status[BEFORE][idx].attr)) {
4351 matchprev = TRUE; /* assume success */
4352 if (tty_status[NOW][idx].dirty) {
4353 /* compare values */
4354 const char *ob, *nb; /* old byte, new byte */
4355 struct WinDesc *cw = wins[WIN_STATUS];
4358 ob = &cw->data[row][c];
4359 nb = status_vals[idx];
4360 while (*nb && c < cw->cols) {
4367 /* if we're not at the end of new string, no match;
4368 we don't need to worry about whether there might
4369 be leftover old string; that could only happen
4370 if they have different lengths, in which case
4371 'update_right' will be set and we won't get here */
4375 if (*nb || (*ob && *ob != ' '
4376 && (*ob != '/' || idx != BL_XP)
4377 && (*ob != '(' || (idx != BL_HP
4378 && idx != BL_ENE))))
4385 if (forcefields || update_right
4386 || (tty_status[NOW][idx].dirty && !matchprev))
4387 tty_status[NOW][idx].redraw = TRUE;
4389 col += tty_status[NOW][idx].lth;
4396 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
4398 status_sanity_check(VOID_ARGS)
4401 static boolean in_sanity_check = FALSE;
4402 static const char *const idxtext[] = {
4403 "BL_TITLE", "BL_STR", "BL_DX", "BL_CO", "BL_IN", "BL_WI", /* 0.. 5 */
4404 "BL_CH","BL_ALIGN", "BL_SCORE", "BL_CAP", "BL_GOLD", /* 6.. 10 */
4405 "BL_ENE", "BL_ENEMAX", "BL_XP", "BL_AC", "BL_HD", /* 11.. 15 */
4406 "BL_TIME", "BL_HUNGER", "BL_HP", "BL_HPMAX", /* 16.. 19 */
4407 "BL_LEVELDESC", "BL_EXP", "BL_CONDITION" /* 20.. 22 */
4410 if (in_sanity_check)
4412 in_sanity_check = TRUE;
4414 * Make sure that every field made it down to the
4415 * bottom of the render_status() for-loop.
4417 for (i = 0; i < MAXBLSTATS; ++i) {
4418 if (tty_status[NOW][i].sanitycheck) {
4419 char panicmsg[BUFSZ];
4421 Sprintf(panicmsg, "failed on tty_status[NOW][%s].", idxtext[i]);
4422 paniclog("status_sanity_check", panicmsg);
4423 tty_status[NOW][i].sanitycheck = FALSE;
4425 * Attention developers: If you encounter the above
4426 * message in paniclog, it almost certainly means that
4427 * a recent code change has caused a failure to reach
4428 * the bottom of render_status(), at least for the BL_
4429 * field identified in the impossible() message.
4431 * That could be because of the addition of a continue
4432 * statement within the render_status() for-loop, or a
4433 * premature return from render_status() before it finished
4434 * its housekeeping chores.
4438 in_sanity_check = FALSE;
4440 #endif /* NHDEVEL_STATUS */
4443 * This is what places a field on the tty display.
4446 tty_putstatusfield(text, x, y)
4450 int i, n, ncols, nrows, lth = 0;
4451 struct WinDesc *cw = 0;
4453 if (WIN_STATUS == WIN_ERR
4454 || (cw = wins[WIN_STATUS]) == (struct WinDesc *) 0)
4455 panic("tty_putstatusfield: Invalid WinDesc\n");
4459 lth = (int) strlen(text);
4461 print_vt_code2(AVTC_SELECT_WINDOW, NHW_STATUS);
4463 if (x < ncols && y < nrows) {
4464 if (x != cw->curx || y != cw->cury)
4465 tty_curs(NHW_STATUS, x, y);
4466 for (i = 0; i < lth; ++i) {
4468 if (n < ncols && *text) {
4470 (void) putchar(*text);
4472 (void) jputchar(*(unsigned char *)text);
4476 cw->data[y][n - 1] = *text;
4483 if (truncation_expected) {
4484 /* Now we're truncating */
4485 ; /* but we knew in advance */
4491 /* caller must set cond_shrinklvl (0..2) before calling us */
4493 set_condition_length()
4498 if (tty_condition_bits) {
4499 for (c = 0; c < SIZE(conditions); ++c) {
4500 mask = conditions[c].mask;
4501 if ((tty_condition_bits & mask) == mask)
4502 lth += 1 + (int) strlen(conditions[c].text[cond_shrinklvl]);
4505 tty_status[NOW][BL_CONDITION].lth = lth;
4512 /* shrink or restore the encumbrance word */
4514 enc_shrinklvl = lvl;
4515 Sprintf(status_vals[BL_CAP], " %s", encvals[lvl][enclev]);
4517 tty_status[NOW][BL_CAP].lth = strlen(status_vals[BL_CAP]);
4524 /* try changing Dlvl: to Dl: */
4526 char *levval = index(status_vals[BL_LEVELDESC], ':');
4529 dlvl_shrinklvl = lvl;
4530 Strcpy(buf, (lvl == 0) ? "Dlvl" : "Dl");
4531 Strcat(buf, levval);
4532 Strcpy(status_vals[BL_LEVELDESC], buf);
4533 tty_status[NOW][BL_LEVELDESC].lth = strlen(status_vals[BL_LEVELDESC]);
4538 * Ensure the underlying status window data start out
4539 * blank and null-terminated.
4542 check_windowdata(VOID_ARGS)
4544 if (WIN_STATUS == WIN_ERR || wins[WIN_STATUS] == (struct WinDesc *) 0) {
4545 paniclog("check_windowdata", " null status window.");
4547 } else if (!windowdata_init) {
4548 tty_clear_nhwindow(WIN_STATUS); /* also sets cw->data[] to spaces */
4549 windowdata_init = TRUE;
4556 * Return what color this condition should
4557 * be displayed in based on user settings.
4560 condcolor(bm, bmarray)
4562 unsigned long *bmarray;
4567 for (i = 0; i < CLR_MAX; ++i) {
4568 if ((bm & bmarray[i]) != 0)
4574 /* might need something more elaborate if some compiler complains that
4575 the condition where this gets used always has the same value */
4576 #define condcolor(bm,bmarray) NO_COLOR
4577 #define term_start_color(color) /*empty*/
4578 #define term_end_color() /*empty*/
4579 #endif /* TEXTCOLOR */
4582 condattr(bm, bmarray)
4584 unsigned long *bmarray;
4589 if (bm && bmarray) {
4590 for (i = HL_ATTCLR_DIM; i < BL_ATTCLR_MAX; ++i) {
4591 if ((bm & bmarray[i]) != 0) {
4596 case HL_ATTCLR_BLINK:
4599 case HL_ATTCLR_ULINE:
4602 case HL_ATTCLR_INVERSE:
4605 case HL_ATTCLR_BOLD:
4615 #define Begin_Attr(m) \
4618 if ((m) & HL_BOLD) \
4619 term_start_attr(ATR_BOLD); \
4620 if ((m) & HL_INVERSE) \
4621 term_start_attr(ATR_INVERSE); \
4622 if ((m) & HL_ULINE) \
4623 term_start_attr(ATR_ULINE); \
4624 if ((m) & HL_BLINK) \
4625 term_start_attr(ATR_BLINK); \
4627 term_start_attr(ATR_DIM); \
4631 #define End_Attr(m) \
4635 term_end_attr(ATR_DIM); \
4636 if ((m) & HL_BLINK) \
4637 term_end_attr(ATR_BLINK); \
4638 if ((m) & HL_ULINE) \
4639 term_end_attr(ATR_ULINE); \
4640 if ((m) & HL_INVERSE) \
4641 term_end_attr(ATR_INVERSE); \
4642 if ((m) & HL_BOLD) \
4643 term_end_attr(ATR_BOLD); \
4648 render_status(VOID_ARGS)
4651 int i, x, y, idx, c, row, tlth, num_rows, coloridx = 0, attrmask = 0;
4653 struct WinDesc *cw = 0;
4655 if (WIN_STATUS == WIN_ERR
4656 || (cw = wins[WIN_STATUS]) == (struct WinDesc *) 0) {
4657 paniclog("render_status", "WIN_ERR on status window.");
4661 num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
4662 for (row = 0; row < num_rows; ++row) {
4665 tty_curs(WIN_STATUS, 1, y);
4666 for (i = 0; (idx = fieldorder[row][i]) != BL_FLUSH; ++i) {
4667 if (!status_activefields[idx])
4669 x = tty_status[NOW][idx].x;
4670 text = status_vals[idx]; /* always "" for BL_CONDITION */
4671 tlth = (int) tty_status[NOW][idx].lth; /* valid for BL_CONDITION */
4673 if (tty_status[NOW][idx].redraw || !do_field_opt) {
4674 boolean hitpointbar = (idx == BL_TITLE
4675 && iflags.wc2_hitpointbar);
4677 if (idx == BL_CONDITION) {
4679 * +-----------------+
4680 * | Condition Codes |
4681 * +-----------------+
4683 bits = tty_condition_bits;
4684 /* if no bits are set, we can fall through condition
4685 rendering code to finalx[] handling (and subsequent
4686 rest-of-line erasure if line is shorter than before) */
4687 if (num_rows == 3 && bits != 0L) {
4689 char *dat = &cw->data[y][0];
4691 /* line up with hunger (or where it would have
4692 been when currently omitted); if there isn't
4693 enough room for that, right justify; or place
4694 as-is if not even enough room for /that/; we
4695 expect hunger to be on preceding row, in which
4696 case its current data has been moved to [BEFORE] */
4697 if (tty_status[BEFORE][BL_HUNGER].y < row
4698 && x < tty_status[BEFORE][BL_HUNGER].x
4699 && (tty_status[BEFORE][BL_HUNGER].x + tlth
4701 k = tty_status[BEFORE][BL_HUNGER].x;
4702 else if (x + tlth < cw->cols - 1)
4703 k = cw->cols - tlth;
4707 if (dat[x - 1] != ' ')
4708 tty_putstatusfield(" ", x, y);
4711 tty_status[NOW][BL_CONDITION].x = x;
4712 tty_curs(WIN_STATUS, x, y);
4714 for (c = 0; c < SIZE(conditions) && bits != 0L; ++c) {
4715 mask = conditions[c].mask;
4717 const char *condtext;
4719 tty_putstatusfield(" ", x++, y);
4720 if (iflags.hilite_delta) {
4721 attrmask = condattr(mask, tty_colormasks);
4722 Begin_Attr(attrmask);
4723 coloridx = condcolor(mask, tty_colormasks);
4724 if (coloridx != NO_COLOR)
4725 term_start_color(coloridx);
4727 condtext = conditions[c].text[cond_shrinklvl];
4728 if (x >= cw->cols && !truncation_expected) {
4730 "Unexpected condition placement overflow for \"%s\"",
4733 bits = 0L; /* skip any remaining conditions */
4735 tty_putstatusfield(condtext, x, y);
4736 x += (int) strlen(condtext);
4737 if (iflags.hilite_delta) {
4738 if (coloridx != NO_COLOR)
4745 /* 'x' is 1-based and 'cols' and 'data' are 0-based,
4746 so x==cols means we just stored in data[N-2] and
4747 are now positioned at data[N-1], the terminator;
4748 that's ok as long as we don't write there */
4750 static unsigned once_only = 0;
4752 if (!truncation_expected && !once_only++)
4753 paniclog("render_status()",
4754 " unexpected truncation.");
4757 } else if (hitpointbar) {
4759 * +-------------------------+
4760 * | Title with Hitpoint Bar |
4761 * +-------------------------+
4763 /* hitpointbar using hp percent calculation */
4764 int bar_len, bar_pos = 0;
4765 char bar[MAXCO], *bar2 = (char *) 0, savedch = '\0';
4766 boolean twoparts = (hpbar_percent < 100);
4768 /* force exactly 30 characters, padded with spaces
4769 if shorter or truncated if longer */
4770 if (strlen(text) != 30) {
4771 Sprintf(bar, "%-30.30s", text);
4772 Strcpy(status_vals[BL_TITLE], bar);
4775 bar_len = (int) strlen(bar); /* always 30 */
4777 /* when at full HP, the whole title will be highlighted;
4778 when injured or dead, there will be a second portion
4779 which is not highlighted */
4781 /* figure out where to separate the two parts */
4782 bar_pos = (bar_len * hpbar_percent) / 100;
4783 if (bar_pos < 1 && hpbar_percent > 0)
4785 if (bar_pos >= bar_len && hpbar_percent < 100)
4786 bar_pos = bar_len - 1;
4787 bar2 = &bar[bar_pos];
4791 tty_putstatusfield("[", x++, y);
4792 if (*bar) { /* always True, unless twoparts+dead (0 HP) */
4793 term_start_attr(ATR_INVERSE);
4794 if (iflags.hilite_delta && hpbar_color != NO_COLOR)
4795 term_start_color(hpbar_color);
4796 tty_putstatusfield(bar, x, y);
4797 x += (int) strlen(bar);
4798 if (iflags.hilite_delta && hpbar_color != NO_COLOR)
4800 term_end_attr(ATR_INVERSE);
4802 if (twoparts) { /* no highlighting for second part */
4804 tty_putstatusfield(bar2, x, y);
4805 x += (int) strlen(bar2);
4807 tty_putstatusfield("]", x++, y);
4810 * +-----------------------------+
4811 * | Everything else that is not |
4812 * | in a special case above |
4813 * +-----------------------------+
4815 if (iflags.hilite_delta) {
4816 while (*text == ' ') {
4817 tty_putstatusfield(" ", x++, y);
4820 if (*text == '/' && idx == BL_EXP) {
4821 tty_putstatusfield("/", x++, y);
4824 attrmask = tty_status[NOW][idx].attr;
4825 Begin_Attr(attrmask);
4826 coloridx = tty_status[NOW][idx].color;
4827 if (coloridx != NO_COLOR)
4828 term_start_color(coloridx);
4830 tty_putstatusfield(text, x, y);
4831 x += (int) strlen(text);
4832 if (iflags.hilite_delta) {
4833 if (coloridx != NO_COLOR)
4839 /* not rendered => same text as before */
4842 finalx[row][NOW] = x - 1;
4843 /* reset .redraw and .dirty now that field has been rendered */
4844 tty_status[NOW][idx].dirty = FALSE;
4845 tty_status[NOW][idx].redraw = FALSE;
4846 tty_status[NOW][idx].sanitycheck = FALSE;
4848 * For comparison of current and previous:
4849 * - Copy the entire tty_status struct.
4851 tty_status[BEFORE][idx] = tty_status[NOW][idx];
4853 x = finalx[row][NOW];
4854 if ((x < finalx[row][BEFORE] || !finalx[row][BEFORE])
4855 && x + 1 < cw->cols) {
4856 tty_curs(WIN_STATUS, x + 1, y);
4860 * For comparison of current and previous:
4861 * - Copy the last written column number on the row.
4863 finalx[row][BEFORE] = finalx[row][NOW];
4868 #endif /* STATUS_HILITES */
4870 #endif /* TTY_GRAPHICS */