1 /* NetHack 3.6 wintty.c $NHDT-Date: 1453514601 2016/01/23 02:03:21 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.124 $ */
2 /* Copyright (c) David Cohrs, 1991 */
3 /* NetHack may be freely redistributed. See license for details. */
6 * Neither a standard out nor character-based control codes should be
7 * part of the "tty look" windowing implementation.
11 /* It's still not clear I've caught all the cases for H2344. #undef this
12 * to back out the changes. */
21 #define MICRO /* The Mac is a MICRO only for this file, not in general! */
23 extern void msmsg(const char *, ...);
33 #ifdef CLIPPING /* might want SIGWINCH */
34 #if defined(BSD) || defined(ULTRIX) || defined(AIX_31) \
35 || defined(_BULL_SOURCE)
40 #ifdef TTY_TILES_ESCCODES
41 extern short glyph2tile
[];
42 #define TILE_ANSI_COMMAND 'z'
43 #define AVTC_GLYPH_START 0
44 #define AVTC_GLYPH_END 1
45 #define AVTC_SELECT_WINDOW 2
46 #define AVTC_INLINE_SYNC 3
49 extern char mapped_menu_cmds
[]; /* from options.c */
51 /* this is only needed until tty_status_* routines are written */
52 extern NEARDATA winid WIN_STATUS
;
54 /* Interface definition, for windows.c */
55 struct window_procs tty_procs
= {
58 WC_TILED_MAP
| WC_ASCII_MAP
|
63 WC_COLOR
| WC_HILITE_PET
| WC_INVERSE
| WC_EIGHT_BIT_IN
,
64 #if defined(SELECTSAVED)
68 tty_init_nhwindows
, tty_player_selection
, tty_askname
, tty_get_nh_event
,
69 tty_exit_nhwindows
, tty_suspend_nhwindows
, tty_resume_nhwindows
,
70 tty_create_nhwindow
, tty_clear_nhwindow
, tty_display_nhwindow
,
71 tty_destroy_nhwindow
, tty_curs
, tty_putstr
, genl_putmixed
,
72 tty_display_file
, tty_start_menu
, tty_add_menu
, tty_end_menu
,
73 tty_select_menu
, tty_message_menu
, tty_update_inventory
, tty_mark_synch
,
79 tty_update_positionbar
,
81 tty_print_glyph
, tty_raw_print
, tty_raw_print_bold
, tty_nhgetch
,
82 tty_nh_poskey
, tty_nhbell
, tty_doprev_message
, tty_yn_function
,
83 tty_getlin
, tty_get_ext_cmd
, tty_number_pad
, tty_delay_output
,
84 #ifdef CHANGE_COLOR /* the Mac uses a palette device */
87 tty_change_background
, set_tty_font_name
,
92 /* other defs that really should go away (they're tty specific) */
93 tty_start_screen
, tty_end_screen
, genl_outrip
,
95 nttty_preference_update
,
97 genl_preference_update
,
99 tty_getmsghistory
, tty_putmsghistory
,
100 #ifdef STATUS_VIA_WINDOWPORT
102 genl_status_finish
, genl_status_enablefield
,
104 #ifdef STATUS_HILITES
105 tty_status_threshold
,
108 genl_can_suspend_yes
,
111 static int maxwin
= 0; /* number of windows in use */
113 struct WinDesc
*wins
[MAXWIN
];
114 struct DisplayDesc
*ttyDisplay
; /* the tty display descriptor */
116 extern void FDECL(cmov
, (int, int)); /* from termcap.c */
117 extern void FDECL(nocmov
, (int, int)); /* from termcap.c */
118 #if defined(UNIX) || defined(VMS)
119 static char obuf
[BUFSIZ
]; /* BUFSIZ is defined in stdio.h */
122 static char winpanicstr
[] = "Bad window id %d";
123 char defmorestr
[] = "--More--";
126 #if defined(USE_TILES) && defined(MSDOS)
127 boolean clipping
= FALSE
; /* clipping on? */
128 int clipx
= 0, clipxmax
= 0;
130 static boolean clipping
= FALSE
; /* clipping on? */
131 static int clipx
= 0, clipxmax
= 0;
133 static int clipy
= 0, clipymax
= 0;
134 #endif /* CLIPPING */
136 #if defined(USE_TILES) && defined(MSDOS)
137 extern void FDECL(adjust_cursor_flags
, (struct WinDesc
*));
140 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
141 boolean GFlag
= FALSE
;
142 boolean HE_resets_AS
; /* see termcap.c */
145 #if defined(MICRO) || defined(WIN32CON)
146 static const char to_continue
[] = "to continue";
147 #define getret() getreturn(to_continue)
149 STATIC_DCL
void NDECL(getret
);
151 STATIC_DCL
void FDECL(erase_menu_or_text
,
152 (winid
, struct WinDesc
*, BOOLEAN_P
));
153 STATIC_DCL
void FDECL(free_window_info
, (struct WinDesc
*, BOOLEAN_P
));
154 STATIC_DCL
void FDECL(dmore
, (struct WinDesc
*, const char *));
155 STATIC_DCL
void FDECL(set_item_state
, (winid
, int, tty_menu_item
*));
156 STATIC_DCL
void FDECL(set_all_on_page
,
157 (winid
, tty_menu_item
*, tty_menu_item
*));
158 STATIC_DCL
void FDECL(unset_all_on_page
,
159 (winid
, tty_menu_item
*, tty_menu_item
*));
160 STATIC_DCL
void FDECL(invert_all_on_page
,
161 (winid
, tty_menu_item
*, tty_menu_item
*, CHAR_P
));
162 STATIC_DCL
void FDECL(invert_all
,
163 (winid
, tty_menu_item
*, tty_menu_item
*, CHAR_P
));
164 STATIC_DCL
void FDECL(process_menu_window
, (winid
, struct WinDesc
*));
165 STATIC_DCL
void FDECL(process_text_window
, (winid
, struct WinDesc
*));
166 STATIC_DCL tty_menu_item
*FDECL(reverse
, (tty_menu_item
*));
167 STATIC_DCL
const char *FDECL(compress_str
, (const char *));
168 STATIC_DCL
void FDECL(tty_putsym
, (winid
, int, int, CHAR_P
));
169 STATIC_DCL
void FDECL(bail
, (const char *)); /* __attribute__((noreturn)) */
170 STATIC_DCL
void FDECL(setup_rolemenu
, (winid
, BOOLEAN_P
, int, int, int));
171 STATIC_DCL
void FDECL(setup_racemenu
, (winid
, BOOLEAN_P
, int, int, int));
172 STATIC_DCL
void FDECL(setup_gendmenu
, (winid
, BOOLEAN_P
, int, int, int));
173 STATIC_DCL
void FDECL(setup_algnmenu
, (winid
, BOOLEAN_P
, int, int, int));
174 STATIC_DCL boolean
NDECL(reset_role_filtering
);
177 * A string containing all the default commands -- to add to a list
178 * of acceptable inputs.
180 static const char default_menu_cmds
[] = {
181 MENU_FIRST_PAGE
, MENU_LAST_PAGE
, MENU_NEXT_PAGE
,
182 MENU_PREVIOUS_PAGE
, MENU_SELECT_ALL
, MENU_UNSELECT_ALL
,
183 MENU_INVERT_ALL
, MENU_SELECT_PAGE
, MENU_UNSELECT_PAGE
,
184 MENU_INVERT_PAGE
, MENU_SEARCH
, 0 /* null terminator */
187 #ifdef TTY_TILES_ESCCODES
188 static int vt_tile_current_window
= -2;
191 print_vt_code(i
, c
, d
)
194 if (iflags
.vt_tiledata
) {
196 if (i
== AVTC_SELECT_WINDOW
) {
197 if (c
== vt_tile_current_window
) return;
198 vt_tile_current_window
= c
;
201 printf("\033[1;%d;%d;%d%c", i
, c
, d
, TILE_ANSI_COMMAND
);
203 printf("\033[1;%d;%d%c", i
, c
, TILE_ANSI_COMMAND
);
205 printf("\033[1;%d%c", i
, TILE_ANSI_COMMAND
);
210 # define print_vt_code(i, c, d) ;
211 #endif /* !TTY_TILES_ESCCODES */
212 #define print_vt_code1(i) print_vt_code((i), -1, -1)
213 #define print_vt_code2(i,c) print_vt_code((i), (c), -1)
214 #define print_vt_code3(i,c,d) print_vt_code((i), (c), (d))
217 /* clean up and quit */
223 tty_exit_nhwindows(mesg
);
224 terminate(EXIT_SUCCESS
);
228 #if defined(SIGWINCH) && defined(CLIPPING)
232 int oldLI
= LI
, oldCO
= CO
, i
;
233 register struct WinDesc
*cw
;
237 #define WINCH_MESSAGE "(SIGWINCH)"
239 (void) write(fileno(wc_tracelogf
), WINCH_MESSAGE
,
240 strlen(WINCH_MESSAGE
));
245 /* For long running events such as multi-page menus and
246 * display_file(), we note the signal's occurance and
247 * hope the code there decides to handle the situation
248 * and reset the flag. There will be race conditions
249 * when handling this - code handlers so it doesn't matter.
254 if ((oldLI
!= LI
|| oldCO
!= CO
) && ttyDisplay
) {
255 ttyDisplay
->rows
= LI
;
256 ttyDisplay
->cols
= CO
;
258 cw
= wins
[BASE_WINDOW
];
259 cw
->rows
= ttyDisplay
->rows
;
260 cw
->cols
= ttyDisplay
->cols
;
262 if (iflags
.window_inited
) {
263 cw
= wins
[WIN_MESSAGE
];
264 cw
->curx
= cw
->cury
= 0;
266 tty_destroy_nhwindow(WIN_STATUS
);
267 WIN_STATUS
= tty_create_nhwindow(NHW_STATUS
);
271 if (CO
< COLNO
|| LI
< ROWNO
+ 3) {
273 tty_cliparound(u
.ux
, u
.uy
);
279 i
= ttyDisplay
->toplin
;
280 ttyDisplay
->toplin
= 0;
283 ttyDisplay
->toplin
= i
;
288 for (i
= WIN_INVEN
; i
< MAXWIN
; i
++)
289 if (wins
[i
] && wins
[i
]->active
) {
291 addtopl("Press Return to continue: ");
294 (void) fflush(stdout
);
305 tty_init_nhwindows(argcp
, argv
)
312 * Remember tty modes, to be restored on exit.
314 * gettty() must be called before tty_startup()
315 * due to ordering of LI/CO settings
316 * tty_startup() must be called before initoptions()
317 * due to ordering of graphics settings
319 #if defined(UNIX) || defined(VMS)
320 setbuf(stdout
, obuf
);
324 /* to port dependant tty setup */
325 tty_startup(&wid
, &hgt
);
326 setftty(); /* calls start_screen */
328 /* set up tty descriptor */
329 ttyDisplay
= (struct DisplayDesc
*) alloc(sizeof(struct DisplayDesc
));
330 ttyDisplay
->toplin
= 0;
331 ttyDisplay
->rows
= hgt
;
332 ttyDisplay
->cols
= wid
;
333 ttyDisplay
->curx
= ttyDisplay
->cury
= 0;
334 ttyDisplay
->inmore
= ttyDisplay
->inread
= ttyDisplay
->intr
= 0;
335 ttyDisplay
->dismiss_more
= 0;
337 ttyDisplay
->color
= NO_COLOR
;
339 ttyDisplay
->attrs
= 0;
341 /* set up the default windows */
342 BASE_WINDOW
= tty_create_nhwindow(NHW_BASE
);
343 wins
[BASE_WINDOW
]->active
= 1;
345 ttyDisplay
->lastwin
= WIN_ERR
;
347 #if defined(SIGWINCH) && defined(CLIPPING) && !defined(NO_SIGNAL)
348 (void) signal(SIGWINCH
, winch
);
351 /* add one a space forward menu command alias */
352 add_menu_cmd_alias(' ', MENU_NEXT_PAGE
);
354 tty_clear_nhwindow(BASE_WINDOW
);
356 tty_putstr(BASE_WINDOW
, 0, "");
357 for (i
= 1; i
<= 4; ++i
)
358 tty_putstr(BASE_WINDOW
, 0, copyright_banner_line(i
));
359 tty_putstr(BASE_WINDOW
, 0, "");
360 tty_display_nhwindow(BASE_WINDOW
, FALSE
);
363 /* try to reduce clutter in the code below... */
364 #define ROLE flags.initrole
365 #define RACE flags.initrace
366 #define GEND flags.initgend
367 #define ALGN flags.initalign
370 tty_player_selection()
372 int i
, k
, n
, choice
, nextpick
;
373 boolean getconfirmation
, picksomething
;
375 char pbuf
[QBUFSZ
], plbuf
[QBUFSZ
];
378 menu_item
*selected
= 0;
380 /* Used to avoid "Is this ok?" if player has already specified all
381 * four facets of role.
382 * Note that rigid_role_checks might force any unspecified facets to
383 * have a specific value, but that will still require confirmation;
384 * player can specify the forced ones if avoiding that is demanded.
386 picksomething
= (ROLE
== ROLE_NONE
|| RACE
== ROLE_NONE
387 || GEND
== ROLE_NONE
|| ALGN
== ROLE_NONE
);
389 * choose randomly without asking for all unspecified facets.
391 if (flags
.randomall
&& picksomething
) {
392 if (ROLE
== ROLE_NONE
)
394 if (RACE
== ROLE_NONE
)
396 if (GEND
== ROLE_NONE
)
398 if (ALGN
== ROLE_NONE
)
402 /* prevent unnecessary prompting if role forces race (samurai) or gender
403 (valkyrie) or alignment (rogue), or race forces alignment (orc), &c */
406 /* Should we randomly pick for the player? */
407 if (ROLE
== ROLE_NONE
|| RACE
== ROLE_NONE
|| GEND
== ROLE_NONE
408 || ALGN
== ROLE_NONE
) {
410 char *prompt
= build_plselection_prompt(pbuf
, QBUFSZ
,
411 ROLE
, RACE
, GEND
, ALGN
);
413 /* this prompt string ends in "[ynaq]?":
414 y - game picks role,&c then asks player to confirm;
415 n - player manually chooses via menu selections;
416 a - like 'y', but skips confirmation and starts game;
419 tty_putstr(BASE_WINDOW
, 0, "");
420 echoline
= wins
[BASE_WINDOW
]->cury
;
421 tty_putstr(BASE_WINDOW
, 0, prompt
);
423 pick4u
= lowc(readchar());
424 if (index(quitchars
, pick4u
))
426 } while (!index(ynaqchars
, pick4u
));
427 if ((int) strlen(prompt
) + 1 < CO
) {
428 /* Echo choice and move back down line */
429 tty_putsym(BASE_WINDOW
, (int) strlen(prompt
) + 1, echoline
,
431 tty_putstr(BASE_WINDOW
, 0, "");
433 /* Otherwise it's hard to tell where to echo, and things are
434 * wrapping a bit messily anyway, so (try to) make sure the next
435 * question shows up well and doesn't get wrapped at the
436 * bottom of the window.
438 tty_clear_nhwindow(BASE_WINDOW
);
440 if (pick4u
!= 'y' && pick4u
!= 'a' && pick4u
!= 'n')
447 if (nextpick
== RS_ROLE
) {
449 /* Select a role, if necessary;
450 we'll try to be compatible with pre-selected
451 race/gender/alignment, but may not succeed. */
453 /* Process the choice */
454 if (pick4u
== 'y' || pick4u
== 'a' || ROLE
== ROLE_RANDOM
) {
455 /* Pick a random role */
456 k
= pick_role(RACE
, GEND
, ALGN
, PICK_RANDOM
);
458 tty_putstr(BASE_WINDOW
, 0, "Incompatible role!");
462 /* Prompt for a role */
463 tty_clear_nhwindow(BASE_WINDOW
);
464 role_selection_prolog(RS_ROLE
, BASE_WINDOW
);
465 win
= create_nhwindow(NHW_MENU
);
467 /* populate the menu with role choices */
468 setup_rolemenu(win
, TRUE
, RACE
, GEND
, ALGN
);
469 /* add miscellaneous menu entries */
470 role_menu_extra(ROLE_RANDOM
, win
, TRUE
);
471 any
.a_int
= 0; /* separator, not a choice */
472 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "",
474 role_menu_extra(RS_RACE
, win
, FALSE
);
475 role_menu_extra(RS_GENDER
, win
, FALSE
);
476 role_menu_extra(RS_ALGNMNT
, win
, FALSE
);
478 role_menu_extra(RS_filter
, win
, FALSE
);
479 role_menu_extra(ROLE_NONE
, win
, FALSE
); /* quit */
480 Strcpy(pbuf
, "Pick a role or profession");
482 n
= select_menu(win
, PICK_ONE
, &selected
);
484 * PICK_ONE with preselected choice behaves strangely:
485 * n == -1 -- <escape>, so use quit choice;
486 * n == 0 -- explicitly chose preselected entry,
487 * toggling it off, so use it;
488 * n == 1 -- implicitly chose preselected entry
489 * with <space> or <return>;
490 * n == 2 -- explicitly chose a different entry, so
491 * both it and preselected one are in list.
494 choice
= selected
[0].item
.a_int
;
495 if (n
> 1 && choice
== ROLE_RANDOM
)
496 choice
= selected
[1].item
.a_int
;
498 choice
= (n
== 0) ? ROLE_RANDOM
: ROLE_NONE
;
500 free((genericptr_t
) selected
), selected
= 0;
501 destroy_nhwindow(win
);
503 if (choice
== ROLE_NONE
) {
504 goto give_up
; /* Selected quit */
505 } else if (choice
== RS_menu_arg(RS_ALGNMNT
)) {
506 ALGN
= k
= ROLE_NONE
;
507 nextpick
= RS_ALGNMNT
;
508 } else if (choice
== RS_menu_arg(RS_GENDER
)) {
509 GEND
= k
= ROLE_NONE
;
510 nextpick
= RS_GENDER
;
511 } else if (choice
== RS_menu_arg(RS_RACE
)) {
512 RACE
= k
= ROLE_NONE
;
514 } else if (choice
== RS_menu_arg(RS_filter
)) {
515 ROLE
= k
= ROLE_NONE
;
516 (void) reset_role_filtering();
518 } else if (choice
== ROLE_RANDOM
) {
519 k
= pick_role(RACE
, GEND
, ALGN
, PICK_RANDOM
);
530 if (nextpick
== RS_RACE
) {
531 nextpick
= (ROLE
< 0) ? RS_ROLE
: RS_GENDER
;
532 /* Select a race, if necessary;
533 force compatibility with role, try for compatibility
534 with pre-selected gender/alignment. */
535 if (RACE
< 0 || !validrace(ROLE
, RACE
)) {
536 /* no race yet, or pre-selected race not valid */
537 if (pick4u
== 'y' || pick4u
== 'a' || RACE
== ROLE_RANDOM
) {
538 k
= pick_race(ROLE
, GEND
, ALGN
, PICK_RANDOM
);
540 tty_putstr(BASE_WINDOW
, 0, "Incompatible race!");
543 } else { /* pick4u == 'n' */
544 /* Count the number of valid races */
545 n
= 0; /* number valid */
546 k
= 0; /* valid race */
547 for (i
= 0; races
[i
].noun
; i
++)
548 if (ok_race(ROLE
, i
, GEND
, ALGN
)) {
553 for (i
= 0; races
[i
].noun
; i
++)
554 if (validrace(ROLE
, i
)) {
559 /* Permit the user to pick, if there is more than one */
561 tty_clear_nhwindow(BASE_WINDOW
);
562 role_selection_prolog(RS_RACE
, BASE_WINDOW
);
563 win
= create_nhwindow(NHW_MENU
);
565 any
= zeroany
; /* zero out all bits */
566 /* populate the menu with role choices */
567 setup_racemenu(win
, TRUE
, ROLE
, GEND
, ALGN
);
568 /* add miscellaneous menu entries */
569 role_menu_extra(ROLE_RANDOM
, win
, TRUE
);
570 any
.a_int
= 0; /* separator, not a choice */
571 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "",
573 role_menu_extra(RS_ROLE
, win
, FALSE
);
574 role_menu_extra(RS_GENDER
, win
, FALSE
);
575 role_menu_extra(RS_ALGNMNT
, win
, FALSE
);
577 role_menu_extra(RS_filter
, win
, FALSE
);
578 role_menu_extra(ROLE_NONE
, win
, FALSE
); /* quit */
579 Strcpy(pbuf
, "Pick a race or species");
581 n
= select_menu(win
, PICK_ONE
, &selected
);
583 choice
= selected
[0].item
.a_int
;
584 if (n
> 1 && choice
== ROLE_RANDOM
)
585 choice
= selected
[1].item
.a_int
;
587 choice
= (n
== 0) ? ROLE_RANDOM
: ROLE_NONE
;
589 free((genericptr_t
) selected
), selected
= 0;
590 destroy_nhwindow(win
);
592 if (choice
== ROLE_NONE
) {
593 goto give_up
; /* Selected quit */
594 } else if (choice
== RS_menu_arg(RS_ALGNMNT
)) {
595 ALGN
= k
= ROLE_NONE
;
596 nextpick
= RS_ALGNMNT
;
597 } else if (choice
== RS_menu_arg(RS_GENDER
)) {
598 GEND
= k
= ROLE_NONE
;
599 nextpick
= RS_GENDER
;
600 } else if (choice
== RS_menu_arg(RS_ROLE
)) {
601 ROLE
= k
= ROLE_NONE
;
603 } else if (choice
== RS_menu_arg(RS_filter
)) {
604 RACE
= k
= ROLE_NONE
;
605 if (reset_role_filtering())
609 } else if (choice
== ROLE_RANDOM
) {
610 k
= pick_race(ROLE
, GEND
, ALGN
, PICK_RANDOM
);
622 if (nextpick
== RS_GENDER
) {
623 nextpick
= (ROLE
< 0) ? RS_ROLE
: (RACE
< 0) ? RS_RACE
625 /* Select a gender, if necessary;
626 force compatibility with role/race, try for compatibility
627 with pre-selected alignment. */
628 if (GEND
< 0 || !validgend(ROLE
, RACE
, GEND
)) {
629 /* no gender yet, or pre-selected gender not valid */
630 if (pick4u
== 'y' || pick4u
== 'a' || GEND
== ROLE_RANDOM
) {
631 k
= pick_gend(ROLE
, RACE
, ALGN
, PICK_RANDOM
);
633 tty_putstr(BASE_WINDOW
, 0, "Incompatible gender!");
634 k
= randgend(ROLE
, RACE
);
636 } else { /* pick4u == 'n' */
637 /* Count the number of valid genders */
638 n
= 0; /* number valid */
639 k
= 0; /* valid gender */
640 for (i
= 0; i
< ROLE_GENDERS
; i
++)
641 if (ok_gend(ROLE
, RACE
, i
, ALGN
)) {
646 for (i
= 0; i
< ROLE_GENDERS
; i
++)
647 if (validgend(ROLE
, RACE
, i
)) {
652 /* Permit the user to pick, if there is more than one */
654 tty_clear_nhwindow(BASE_WINDOW
);
655 role_selection_prolog(RS_GENDER
, BASE_WINDOW
);
656 win
= create_nhwindow(NHW_MENU
);
658 any
= zeroany
; /* zero out all bits */
659 /* populate the menu with gender choices */
660 setup_gendmenu(win
, TRUE
, ROLE
, RACE
, ALGN
);
661 /* add miscellaneous menu entries */
662 role_menu_extra(ROLE_RANDOM
, win
, TRUE
);
663 any
.a_int
= 0; /* separator, not a choice */
664 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "",
666 role_menu_extra(RS_ROLE
, win
, FALSE
);
667 role_menu_extra(RS_RACE
, win
, FALSE
);
668 role_menu_extra(RS_ALGNMNT
, win
, FALSE
);
670 role_menu_extra(RS_filter
, win
, FALSE
);
671 role_menu_extra(ROLE_NONE
, win
, FALSE
); /* quit */
672 Strcpy(pbuf
, "Pick a gender or sex");
674 n
= select_menu(win
, PICK_ONE
, &selected
);
676 choice
= selected
[0].item
.a_int
;
677 if (n
> 1 && choice
== ROLE_RANDOM
)
678 choice
= selected
[1].item
.a_int
;
680 choice
= (n
== 0) ? ROLE_RANDOM
: ROLE_NONE
;
682 free((genericptr_t
) selected
), selected
= 0;
683 destroy_nhwindow(win
);
685 if (choice
== ROLE_NONE
) {
686 goto give_up
; /* Selected quit */
687 } else if (choice
== RS_menu_arg(RS_ALGNMNT
)) {
688 ALGN
= k
= ROLE_NONE
;
689 nextpick
= RS_ALGNMNT
;
690 } else if (choice
== RS_menu_arg(RS_RACE
)) {
691 RACE
= k
= ROLE_NONE
;
693 } else if (choice
== RS_menu_arg(RS_ROLE
)) {
694 ROLE
= k
= ROLE_NONE
;
696 } else if (choice
== RS_menu_arg(RS_filter
)) {
697 GEND
= k
= ROLE_NONE
;
698 if (reset_role_filtering())
701 nextpick
= RS_GENDER
;
702 } else if (choice
== ROLE_RANDOM
) {
703 k
= pick_gend(ROLE
, RACE
, ALGN
, PICK_RANDOM
);
705 k
= randgend(ROLE
, RACE
);
712 } /* needed gender */
713 } /* picking gender */
715 if (nextpick
== RS_ALGNMNT
) {
716 nextpick
= (ROLE
< 0) ? RS_ROLE
: (RACE
< 0) ? RS_RACE
: RS_GENDER
;
717 /* Select an alignment, if necessary;
718 force compatibility with role/race/gender. */
719 if (ALGN
< 0 || !validalign(ROLE
, RACE
, ALGN
)) {
720 /* no alignment yet, or pre-selected alignment not valid */
721 if (pick4u
== 'y' || pick4u
== 'a' || ALGN
== ROLE_RANDOM
) {
722 k
= pick_align(ROLE
, RACE
, GEND
, PICK_RANDOM
);
724 tty_putstr(BASE_WINDOW
, 0, "Incompatible alignment!");
725 k
= randalign(ROLE
, RACE
);
727 } else { /* pick4u == 'n' */
728 /* Count the number of valid alignments */
729 n
= 0; /* number valid */
730 k
= 0; /* valid alignment */
731 for (i
= 0; i
< ROLE_ALIGNS
; i
++)
732 if (ok_align(ROLE
, RACE
, GEND
, i
)) {
737 for (i
= 0; i
< ROLE_ALIGNS
; i
++)
738 if (validalign(ROLE
, RACE
, i
)) {
743 /* Permit the user to pick, if there is more than one */
745 tty_clear_nhwindow(BASE_WINDOW
);
746 role_selection_prolog(RS_ALGNMNT
, BASE_WINDOW
);
747 win
= create_nhwindow(NHW_MENU
);
749 any
= zeroany
; /* zero out all bits */
750 setup_algnmenu(win
, TRUE
, ROLE
, RACE
, GEND
);
751 role_menu_extra(ROLE_RANDOM
, win
, TRUE
);
752 any
.a_int
= 0; /* separator, not a choice */
753 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "",
755 role_menu_extra(RS_ROLE
, win
, FALSE
);
756 role_menu_extra(RS_RACE
, win
, FALSE
);
757 role_menu_extra(RS_GENDER
, win
, FALSE
);
759 role_menu_extra(RS_filter
, win
, FALSE
);
760 role_menu_extra(ROLE_NONE
, win
, FALSE
); /* quit */
761 Strcpy(pbuf
, "Pick an alignment or creed");
763 n
= select_menu(win
, PICK_ONE
, &selected
);
765 choice
= selected
[0].item
.a_int
;
766 if (n
> 1 && choice
== ROLE_RANDOM
)
767 choice
= selected
[1].item
.a_int
;
769 choice
= (n
== 0) ? ROLE_RANDOM
: ROLE_NONE
;
771 free((genericptr_t
) selected
), selected
= 0;
772 destroy_nhwindow(win
);
774 if (choice
== ROLE_NONE
) {
775 goto give_up
; /* Selected quit */
776 } else if (choice
== RS_menu_arg(RS_GENDER
)) {
777 GEND
= k
= ROLE_NONE
;
778 nextpick
= RS_GENDER
;
779 } else if (choice
== RS_menu_arg(RS_RACE
)) {
780 RACE
= k
= ROLE_NONE
;
782 } else if (choice
== RS_menu_arg(RS_ROLE
)) {
783 ROLE
= k
= ROLE_NONE
;
785 } else if (choice
== RS_menu_arg(RS_filter
)) {
786 ALGN
= k
= ROLE_NONE
;
787 if (reset_role_filtering())
790 nextpick
= RS_ALGNMNT
;
791 } else if (choice
== ROLE_RANDOM
) {
792 k
= pick_align(ROLE
, RACE
, GEND
, PICK_RANDOM
);
794 k
= randalign(ROLE
, RACE
);
801 } /* needed alignment */
802 } /* picking alignment */
804 } while (ROLE
< 0 || RACE
< 0 || GEND
< 0 || ALGN
< 0);
807 * Role, race, &c have now been determined;
808 * ask for confirmation and maybe go back to choose all over again.
810 * Uses ynaq for familiarity, although 'a' is usually a
811 * superset of 'y' but here is an alternate form of 'n'.
813 * title: Is this ok? [ynaq]
815 * text: $name, $alignment $gender $race $role
817 * menu: y + yes; play
819 * maybe: a - no; rename hero
823 getconfirmation
= (picksomething
&& pick4u
!= 'a' && !flags
.randomall
);
824 while (getconfirmation
) {
825 tty_clear_nhwindow(BASE_WINDOW
);
826 role_selection_prolog(ROLE_NONE
, BASE_WINDOW
);
827 win
= create_nhwindow(NHW_MENU
);
829 any
= zeroany
; /* zero out all bits */
831 if (!roles
[ROLE
].name
.f
832 && (roles
[ROLE
].allow
& ROLE_GENDMASK
)
833 == (ROLE_MALE
| ROLE_FEMALE
))
834 Sprintf(plbuf
, " %s", genders
[GEND
].adj
);
836 *plbuf
= '\0'; /* omit redundant gender */
837 Sprintf(pbuf
, "%s, %s%s %s %s", plname
, aligns
[ALGN
].adj
, plbuf
,
839 (GEND
== 1 && roles
[ROLE
].name
.f
) ? roles
[ROLE
].name
.f
840 : roles
[ROLE
].name
.m
);
841 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, pbuf
,
843 /* blank separator */
845 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "", MENU_UNSELECTED
);
846 /* [ynaq] menu choices */
848 add_menu(win
, NO_GLYPH
, &any
, 'y', 0, ATR_NONE
, "Yes; start game",
851 add_menu(win
, NO_GLYPH
, &any
, 'n', 0, ATR_NONE
,
852 "No; choose role again", MENU_UNSELECTED
);
853 if (iflags
.renameallowed
) {
855 add_menu(win
, NO_GLYPH
, &any
, 'a', 0, ATR_NONE
,
856 "Not yet; choose another name", MENU_UNSELECTED
);
859 add_menu(win
, NO_GLYPH
, &any
, 'q', 0, ATR_NONE
, "Quit",
861 Sprintf(pbuf
, "Is this ok? [yn%sq]", iflags
.renameallowed
? "a" : "");
863 n
= select_menu(win
, PICK_ONE
, &selected
);
864 /* [pick-one menus with a preselected entry behave oddly...] */
865 choice
= (n
> 0) ? selected
[n
- 1].item
.a_int
: (n
== 0) ? 1 : -1;
867 free((genericptr_t
) selected
), selected
= 0;
868 destroy_nhwindow(win
);
871 default: /* 'q' or ESC */
872 goto give_up
; /* quit */
876 * TODO: what, if anything, should be done if the name is
877 * changed to or from "wizard" after port-specific startup
878 * code has set flags.debug based on the original name?
880 int saveROLE
, saveRACE
, saveGEND
, saveALGN
;
882 iflags
.renameinprogress
= TRUE
;
883 /* plnamesuffix() can change any or all of ROLE, RACE,
884 GEND, ALGN; we'll override that and honor only the name */
885 saveROLE
= ROLE
, saveRACE
= RACE
, saveGEND
= GEND
,
888 plnamesuffix(); /* calls askname() when plname[] is empty */
889 ROLE
= saveROLE
, RACE
= saveRACE
, GEND
= saveGEND
,
891 break; /* getconfirmation is still True */
894 /* start fresh, but bypass "shall I pick everything for you?"
895 step; any partial role selection via config file, command
896 line, or name suffix is discarded this time */
898 ROLE
= RACE
= GEND
= ALGN
= ROLE_NONE
;
901 case 1: /* 'y' or Space or Return/Enter */
902 /* success; drop out through end of function */
903 getconfirmation
= FALSE
;
909 tty_display_nhwindow(BASE_WINDOW
, FALSE
);
915 free((genericptr_t
) selected
); /* [obsolete] */
922 reset_role_filtering()
927 menu_item
*selected
= 0;
929 win
= create_nhwindow(NHW_MENU
);
933 /* no extra blank line preceding this entry; end_menu supplies one */
934 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
935 "Unacceptable roles", MENU_UNSELECTED
);
936 setup_rolemenu(win
, FALSE
, ROLE_NONE
, ROLE_NONE
, ROLE_NONE
);
938 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "", MENU_UNSELECTED
);
939 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
940 "Unacceptable races", MENU_UNSELECTED
);
941 setup_racemenu(win
, FALSE
, ROLE_NONE
, ROLE_NONE
, ROLE_NONE
);
943 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "", MENU_UNSELECTED
);
944 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
945 "Unacceptable genders", MENU_UNSELECTED
);
946 setup_gendmenu(win
, FALSE
, ROLE_NONE
, ROLE_NONE
, ROLE_NONE
);
948 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "", MENU_UNSELECTED
);
949 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
950 "Uncceptable alignments", MENU_UNSELECTED
);
951 setup_algnmenu(win
, FALSE
, ROLE_NONE
, ROLE_NONE
, ROLE_NONE
);
953 end_menu(win
, "Pick all that apply");
954 n
= select_menu(win
, PICK_ANY
, &selected
);
958 for (i
= 0; i
< n
; i
++)
959 setrolefilter(selected
[i
].item
.a_string
);
961 ROLE
= RACE
= GEND
= ALGN
= ROLE_NONE
;
964 free((genericptr_t
) selected
), selected
= 0;
965 destroy_nhwindow(win
);
966 return (n
> 0) ? TRUE
: FALSE
;
974 /* add entries a-Archeologist, b-Barbarian, &c to menu being built in 'win' */
976 setup_rolemenu(win
, filtering
, race
, gend
, algn
)
978 boolean filtering
; /* True => exclude filtered roles; False => filter reset */
979 int race
, gend
, algn
; /* all ROLE_NONE for !filtering case */
984 char thisch
, lastch
= '\0', rolenamebuf
[50];
986 any
= zeroany
; /* zero out all bits */
987 for (i
= 0; roles
[i
].name
.m
; i
++) {
988 role_ok
= ok_role(i
, race
, gend
, algn
);
989 if (filtering
&& !role_ok
)
994 any
.a_string
= roles
[i
].name
.m
;
995 thisch
= lowc(*roles
[i
].name
.m
);
996 if (thisch
== lastch
)
997 thisch
= highc(thisch
);
998 Strcpy(rolenamebuf
, roles
[i
].name
.m
);
999 if (roles
[i
].name
.f
) {
1000 /* role has distinct name for female (C,P) */
1002 /* female already chosen; replace male name */
1003 Strcpy(rolenamebuf
, roles
[i
].name
.f
);
1004 } else if (gend
< 0) {
1005 /* not chosen yet; append slash+female name */
1006 Strcat(rolenamebuf
, "/");
1007 Strcat(rolenamebuf
, roles
[i
].name
.f
);
1010 /* !filtering implies reset_role_filtering() where we want to
1011 mark this role as preseleted if current filter excludes it */
1012 add_menu(win
, NO_GLYPH
, &any
, thisch
, 0, ATR_NONE
, an(rolenamebuf
),
1013 (!filtering
&& !role_ok
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1019 setup_racemenu(win
, filtering
, role
, gend
, algn
)
1022 int role
, gend
, algn
;
1030 for (i
= 0; races
[i
].noun
; i
++) {
1031 race_ok
= ok_race(role
, i
, gend
, algn
);
1032 if (filtering
&& !race_ok
)
1037 any
.a_string
= races
[i
].noun
;
1038 this_ch
= *races
[i
].noun
;
1039 /* filtering: picking race, so choose by first letter, with
1040 capital letter as unseen accelerator;
1041 !filtering: resetting filter rather than picking, choose by
1042 capital letter since lowercase role letters will be present */
1043 add_menu(win
, NO_GLYPH
, &any
,
1044 filtering
? this_ch
: highc(this_ch
),
1045 filtering
? highc(this_ch
) : 0,
1046 ATR_NONE
, races
[i
].noun
,
1047 (!filtering
&& !race_ok
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1052 setup_gendmenu(win
, filtering
, role
, race
, algn
)
1055 int role
, race
, algn
;
1063 for (i
= 0; i
< ROLE_GENDERS
; i
++) {
1064 gend_ok
= ok_gend(role
, race
, i
, algn
);
1065 if (filtering
&& !gend_ok
)
1070 any
.a_string
= genders
[i
].adj
;
1071 this_ch
= *genders
[i
].adj
;
1072 /* (see setup_racemenu for explanation of selector letters
1073 and setup_rolemenu for preselection) */
1074 add_menu(win
, NO_GLYPH
, &any
,
1075 filtering
? this_ch
: highc(this_ch
),
1076 filtering
? highc(this_ch
) : 0,
1077 ATR_NONE
, genders
[i
].adj
,
1078 (!filtering
&& !gend_ok
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1083 setup_algnmenu(win
, filtering
, role
, race
, gend
)
1086 int role
, race
, gend
;
1094 for (i
= 0; i
< ROLE_ALIGNS
; i
++) {
1095 algn_ok
= ok_align(role
, race
, gend
, i
);
1096 if (filtering
&& !algn_ok
)
1101 any
.a_string
= aligns
[i
].adj
;
1102 this_ch
= *aligns
[i
].adj
;
1103 /* (see setup_racemenu for explanation of selector letters
1104 and setup_rolemenu for preselection) */
1105 add_menu(win
, NO_GLYPH
, &any
,
1106 filtering
? this_ch
: highc(this_ch
),
1107 filtering
? highc(this_ch
) : 0,
1108 ATR_NONE
, aligns
[i
].adj
,
1109 (!filtering
&& !algn_ok
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1114 * plname is filled either by an option (-u Player or -uPlayer) or
1115 * explicitly (by being the wizard) or by askname.
1116 * It may still contain a suffix denoting the role, etc.
1117 * Always called after init_nhwindows() and before display_gamewindows().
1122 static const char who_are_you
[] = "Who are you? ";
1123 register int c
, ct
, tryct
= 0;
1126 if (iflags
.wc2_selectsaved
&& !iflags
.renameinprogress
)
1127 switch (restore_menu(BASE_WINDOW
)) {
1129 bail("Until next time then..."); /* quit */
1132 break; /* no game chosen; start new game */
1134 return; /* plname[] has been set */
1136 #endif /* SELECTSAVED */
1138 tty_putstr(BASE_WINDOW
, 0, "");
1142 bail("Giving up after 10 tries.\n");
1143 tty_curs(BASE_WINDOW
, 1, wins
[BASE_WINDOW
]->cury
- 1);
1144 tty_putstr(BASE_WINDOW
, 0, "Enter a name for your character...");
1145 /* erase previous prompt (in case of ESC after partial response)
1147 tty_curs(BASE_WINDOW
, 1, wins
[BASE_WINDOW
]->cury
), cl_end();
1149 tty_putstr(BASE_WINDOW
, 0, who_are_you
);
1150 tty_curs(BASE_WINDOW
, (int) (sizeof who_are_you
),
1151 wins
[BASE_WINDOW
]->cury
- 1);
1153 while ((c
= tty_nhgetch()) != '\n') {
1159 } /* continue outer loop */
1160 #if defined(WIN32CON)
1162 bail("^C abort.\n");
1164 /* some people get confused when their erase char is not ^H */
1165 if (c
== '\b' || c
== '\177') {
1171 #if defined(MICRO) || defined(WIN32CON)
1172 #if defined(WIN32CON) || defined(MSDOS)
1173 backsp(); /* \b is visible on NT */
1174 (void) putchar(' ');
1180 (void) putchar('\b');
1181 (void) putchar(' ');
1182 (void) putchar('\b');
1187 #if defined(UNIX) || defined(VMS)
1188 if (c
!= '-' && c
!= '@')
1189 if (!(c
>= 'a' && c
<= 'z') && !(c
>= 'A' && c
<= 'Z') &&
1190 /* reject leading digit but allow digits elsewhere
1191 (avoids ambiguity when character name gets
1192 appended to uid to construct save file name) */
1193 !(c
>= '0' && c
<= '9' && ct
> 0))
1196 if (ct
< (int) (sizeof plname
) - 1) {
1199 if (iflags
.grmode
) {
1216 /* move to next line to simulate echo of user's <return> */
1217 tty_curs(BASE_WINDOW
, 1, wins
[BASE_WINDOW
]->cury
+ 1);
1219 /* since we let user pick an arbitrary name now, he/she can pick
1220 another one during role selection */
1221 iflags
.renameallowed
= TRUE
;
1230 #if !defined(MICRO) && !defined(WIN32CON)
1238 xputs(iflags
.cbreak
? "space" : "return");
1239 xputs(" to continue: ");
1247 tty_suspend_nhwindows(str
)
1250 settty(str
); /* calls end_screen, perhaps raw_print */
1252 tty_raw_print(""); /* calls fflush(stdout) */
1256 tty_resume_nhwindows()
1259 setftty(); /* calls start_screen */
1264 tty_exit_nhwindows(str
)
1269 tty_suspend_nhwindows(str
);
1271 * Disable windows to avoid calls to window routines.
1273 free_pickinv_cache(); /* reset its state as well as tear down window */
1274 for (i
= 0; i
< MAXWIN
; i
++) {
1275 if (i
== BASE_WINDOW
)
1276 continue; /* handle wins[BASE_WINDOW] last */
1278 #ifdef FREE_ALL_MEMORY
1279 free_window_info(wins
[i
], TRUE
);
1280 free((genericptr_t
) wins
[i
]);
1282 wins
[i
] = (struct WinDesc
*) 0;
1285 WIN_MAP
= WIN_MESSAGE
= WIN_INVEN
= WIN_ERR
; /* these are all gone now */
1286 #ifndef STATUS_VIA_WINDOWPORT
1287 WIN_STATUS
= WIN_ERR
;
1289 #ifdef FREE_ALL_MEMORY
1290 if (BASE_WINDOW
!= WIN_ERR
&& wins
[BASE_WINDOW
]) {
1291 free_window_info(wins
[BASE_WINDOW
], TRUE
);
1292 free((genericptr_t
) wins
[BASE_WINDOW
]);
1293 wins
[BASE_WINDOW
] = (struct WinDesc
*) 0;
1294 BASE_WINDOW
= WIN_ERR
;
1296 free((genericptr_t
) ttyDisplay
);
1297 ttyDisplay
= (struct DisplayDesc
*) 0;
1300 #ifndef NO_TERMS /*(until this gets added to the window interface)*/
1301 tty_shutdown(); /* cleanup termcap/terminfo/whatever */
1303 iflags
.window_inited
= 0;
1307 tty_create_nhwindow(type
)
1310 struct WinDesc
*newwin
;
1314 if (maxwin
== MAXWIN
)
1317 newwin
= (struct WinDesc
*) alloc(sizeof(struct WinDesc
));
1318 newwin
->type
= type
;
1320 newwin
->active
= FALSE
;
1321 newwin
->curx
= newwin
->cury
= 0;
1322 newwin
->morestr
= 0;
1323 newwin
->mlist
= (tty_menu_item
*) 0;
1324 newwin
->plist
= (tty_menu_item
**) 0;
1325 newwin
->npages
= newwin
->plist_size
= newwin
->nitems
= newwin
->how
= 0;
1328 /* base window, used for absolute movement on the screen */
1329 newwin
->offx
= newwin
->offy
= 0;
1330 newwin
->rows
= ttyDisplay
->rows
;
1331 newwin
->cols
= ttyDisplay
->cols
;
1332 newwin
->maxrow
= newwin
->maxcol
= 0;
1335 /* message window, 1 line long, very wide, top of screen */
1336 newwin
->offx
= newwin
->offy
= 0;
1338 if (iflags
.msg_history
< 20)
1339 iflags
.msg_history
= 20;
1340 else if (iflags
.msg_history
> 60)
1341 iflags
.msg_history
= 60;
1342 newwin
->maxrow
= newwin
->rows
= iflags
.msg_history
;
1343 newwin
->maxcol
= newwin
->cols
= 0;
1346 /* status window, 2 lines long, full width, bottom of screen */
1348 #if defined(USE_TILES) && defined(MSDOS)
1349 if (iflags
.grmode
) {
1350 newwin
->offy
= ttyDisplay
->rows
- 2;
1353 newwin
->offy
= min((int) ttyDisplay
->rows
- 2, ROWNO
+ 1);
1354 newwin
->rows
= newwin
->maxrow
= 2;
1355 newwin
->cols
= newwin
->maxcol
= ttyDisplay
->cols
;
1358 /* map window, ROWNO lines long, full width, below message window */
1361 newwin
->rows
= ROWNO
;
1362 newwin
->cols
= COLNO
;
1363 newwin
->maxrow
= 0; /* no buffering done -- let gbuf do it */
1368 /* inventory/menu window, variable length, full width, top of screen
1370 /* help window, the same, different semantics for display, etc */
1371 newwin
->offx
= newwin
->offy
= 0;
1373 newwin
->cols
= ttyDisplay
->cols
;
1374 newwin
->maxrow
= newwin
->maxcol
= 0;
1377 panic("Tried to create window type %d\n", (int) type
);
1381 for (newid
= 0; newid
< MAXWIN
; newid
++) {
1382 if (wins
[newid
] == 0) {
1383 wins
[newid
] = newwin
;
1387 if (newid
== MAXWIN
) {
1388 panic("No window slots!");
1392 if (newwin
->maxrow
) {
1394 (char **) alloc(sizeof(char *) * (unsigned) newwin
->maxrow
);
1396 (short *) alloc(sizeof(short) * (unsigned) newwin
->maxrow
);
1397 if (newwin
->maxcol
) {
1399 for (i
= 0; i
< newwin
->maxrow
; i
++) {
1400 newwin
->data
[i
] = (char *) alloc((unsigned) newwin
->maxcol
);
1401 newwin
->datlen
[i
] = (short) newwin
->maxcol
;
1404 for (i
= 0; i
< newwin
->maxrow
; i
++) {
1405 newwin
->data
[i
] = (char *) 0;
1406 newwin
->datlen
[i
] = 0;
1409 if (newwin
->type
== NHW_MESSAGE
)
1412 newwin
->data
= (char **) 0;
1413 newwin
->datlen
= (short *) 0;
1420 erase_menu_or_text(window
, cw
, clear
)
1427 tty_curs(window
, 1, 0);
1434 docorner((int) cw
->offx
, cw
->maxrow
+ 1);
1438 free_window_info(cw
, free_data
)
1445 if (cw
== wins
[WIN_MESSAGE
] && cw
->rows
> cw
->maxrow
)
1446 cw
->maxrow
= cw
->rows
; /* topl data */
1447 for (i
= 0; i
< cw
->maxrow
; i
++)
1449 free((genericptr_t
) cw
->data
[i
]);
1450 cw
->data
[i
] = (char *) 0;
1455 free((genericptr_t
) cw
->data
);
1456 cw
->data
= (char **) 0;
1458 free((genericptr_t
) cw
->datlen
);
1459 cw
->datlen
= (short *) 0;
1463 cw
->maxrow
= cw
->maxcol
= 0;
1465 tty_menu_item
*temp
;
1467 while ((temp
= cw
->mlist
) != 0) {
1468 cw
->mlist
= cw
->mlist
->next
;
1470 free((genericptr_t
) temp
->str
);
1471 free((genericptr_t
) temp
);
1475 free((genericptr_t
) cw
->plist
);
1478 cw
->plist_size
= cw
->npages
= cw
->nitems
= cw
->how
= 0;
1480 free((genericptr_t
) cw
->morestr
);
1486 tty_clear_nhwindow(window
)
1489 register struct WinDesc
*cw
= 0;
1491 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0)
1492 panic(winpanicstr
, window
);
1493 ttyDisplay
->lastwin
= window
;
1495 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
1499 if (ttyDisplay
->toplin
) {
1503 docorner(1, cw
->cury
+ 1);
1504 ttyDisplay
->toplin
= 0;
1508 tty_curs(window
, 1, 0);
1510 tty_curs(window
, 1, 1);
1514 /* cheap -- clear the whole thing and tell nethack to redraw botl */
1523 erase_menu_or_text(window
, cw
, TRUE
);
1524 free_window_info(cw
, FALSE
);
1527 cw
->curx
= cw
->cury
= 0;
1531 toggle_menu_curr(window
, curr
, lineno
, in_view
, counting
, count
)
1533 tty_menu_item
*curr
;
1535 boolean in_view
, counting
;
1538 if (curr
->selected
) {
1539 if (counting
&& count
> 0) {
1540 curr
->count
= count
;
1542 set_item_state(window
, lineno
, curr
);
1544 } else { /* change state */
1545 curr
->selected
= FALSE
;
1548 set_item_state(window
, lineno
, curr
);
1551 } else { /* !selected */
1552 if (counting
&& count
> 0) {
1553 curr
->count
= count
;
1554 curr
->selected
= TRUE
;
1556 set_item_state(window
, lineno
, curr
);
1558 } else if (!counting
) {
1559 curr
->selected
= TRUE
;
1561 set_item_state(window
, lineno
, curr
);
1564 /* do nothing counting&&count==0 */
1571 register struct WinDesc
*cw
;
1572 const char *s
; /* valid responses */
1574 const char *prompt
= cw
->morestr
? cw
->morestr
: defmorestr
;
1575 int offset
= (cw
->type
== NHW_TEXT
) ? 1 : 2;
1577 tty_curs(BASE_WINDOW
, (int) ttyDisplay
->curx
+ offset
,
1578 (int) ttyDisplay
->cury
);
1582 ttyDisplay
->curx
+= strlen(prompt
);
1590 set_item_state(window
, lineno
, item
)
1593 tty_menu_item
*item
;
1595 char ch
= item
->selected
? (item
->count
== -1L ? '+' : '#') : '-';
1597 tty_curs(window
, 4, lineno
);
1598 term_start_attr(item
->attr
);
1601 term_end_attr(item
->attr
);
1605 set_all_on_page(window
, page_start
, page_end
)
1607 tty_menu_item
*page_start
, *page_end
;
1609 tty_menu_item
*curr
;
1612 for (n
= 0, curr
= page_start
; curr
!= page_end
; n
++, curr
= curr
->next
)
1613 if (curr
->identifier
.a_void
&& !curr
->selected
) {
1614 curr
->selected
= TRUE
;
1615 set_item_state(window
, n
, curr
);
1620 unset_all_on_page(window
, page_start
, page_end
)
1622 tty_menu_item
*page_start
, *page_end
;
1624 tty_menu_item
*curr
;
1627 for (n
= 0, curr
= page_start
; curr
!= page_end
; n
++, curr
= curr
->next
)
1628 if (curr
->identifier
.a_void
&& curr
->selected
) {
1629 curr
->selected
= FALSE
;
1631 set_item_state(window
, n
, curr
);
1636 invert_all_on_page(window
, page_start
, page_end
, acc
)
1638 tty_menu_item
*page_start
, *page_end
;
1639 char acc
; /* group accelerator, 0 => all */
1641 tty_menu_item
*curr
;
1644 for (n
= 0, curr
= page_start
; curr
!= page_end
; n
++, curr
= curr
->next
)
1645 if (curr
->identifier
.a_void
&& (acc
== 0 || curr
->gselector
== acc
)) {
1646 if (curr
->selected
) {
1647 curr
->selected
= FALSE
;
1650 curr
->selected
= TRUE
;
1651 set_item_state(window
, n
, curr
);
1656 * Invert all entries that match the give group accelerator (or all if zero).
1659 invert_all(window
, page_start
, page_end
, acc
)
1661 tty_menu_item
*page_start
, *page_end
;
1662 char acc
; /* group accelerator, 0 => all */
1664 tty_menu_item
*curr
;
1665 boolean on_curr_page
;
1666 struct WinDesc
*cw
= wins
[window
];
1668 invert_all_on_page(window
, page_start
, page_end
, acc
);
1670 /* invert the rest */
1671 for (on_curr_page
= FALSE
, curr
= cw
->mlist
; curr
; curr
= curr
->next
) {
1672 if (curr
== page_start
)
1673 on_curr_page
= TRUE
;
1674 else if (curr
== page_end
)
1675 on_curr_page
= FALSE
;
1677 if (!on_curr_page
&& curr
->identifier
.a_void
1678 && (acc
== 0 || curr
->gselector
== acc
)) {
1679 if (curr
->selected
) {
1680 curr
->selected
= FALSE
;
1683 curr
->selected
= TRUE
;
1689 process_menu_window(window
, cw
)
1693 tty_menu_item
*page_start
, *page_end
, *curr
;
1695 int n
, curr_page
, page_lines
, resp_len
;
1696 boolean finished
, counting
, reset_count
;
1697 char *cp
, *rp
, resp
[QBUFSZ
], gacc
[QBUFSZ
], *msave
, *morestr
, really_morc
;
1698 #define MENU_EXPLICIT_CHOICE 0x7f /* pseudo menu manipulation char */
1700 curr_page
= page_lines
= 0;
1701 page_start
= page_end
= 0;
1702 msave
= cw
->morestr
; /* save the morestr */
1703 cw
->morestr
= morestr
= (char *) alloc((unsigned) QBUFSZ
);
1709 /* collect group accelerators; for PICK_NONE, they're ignored;
1710 for PICK_ONE, only those which match exactly one entry will be
1711 accepted; for PICK_ANY, those which match any entry are okay */
1713 if (cw
->how
!= PICK_NONE
) {
1715 #define GSELIDX(c) (c & 127) /* guard against `signed char' */
1717 for (i
= 0; i
< SIZE(gcnt
); i
++)
1719 for (n
= 0, curr
= cw
->mlist
; curr
; curr
= curr
->next
)
1720 if (curr
->gselector
&& curr
->gselector
!= curr
->selector
) {
1722 ++gcnt
[GSELIDX(curr
->gselector
)];
1725 if (n
> 0) /* at least one group accelerator found */
1726 for (rp
= gacc
, curr
= cw
->mlist
; curr
; curr
= curr
->next
)
1727 if (curr
->gselector
&& curr
->gselector
!= curr
->selector
1728 && !index(gacc
, curr
->gselector
)
1729 && (cw
->how
== PICK_ANY
1730 || gcnt
[GSELIDX(curr
->gselector
)] == 1)) {
1731 *rp
++ = curr
->gselector
;
1732 *rp
= '\0'; /* re-terminate for index() */
1735 resp_len
= 0; /* lint suppression */
1737 /* loop until finished */
1746 /* new page to be displayed */
1747 if (curr_page
< 0 || (cw
->npages
> 0 && curr_page
>= cw
->npages
))
1748 panic("bad menu screen page #%d", curr_page
);
1751 if (!cw
->offx
) { /* if not corner, do clearscreen */
1753 tty_curs(window
, 1, 0);
1760 if (cw
->npages
> 0) {
1761 /* collect accelerators */
1762 page_start
= cw
->plist
[curr_page
];
1763 page_end
= cw
->plist
[curr_page
+ 1];
1764 for (page_lines
= 0, curr
= page_start
; curr
!= page_end
;
1765 page_lines
++, curr
= curr
->next
) {
1766 int color
= NO_COLOR
, attr
= ATR_NONE
;
1767 boolean menucolr
= FALSE
;
1769 *rp
++ = curr
->selector
;
1771 tty_curs(window
, 1, page_lines
);
1775 (void) putchar(' ');
1778 * Don't use xputs() because (1) under unix it calls
1779 * tputstr() which will interpret a '*' as some kind
1780 * of padding information and (2) it calls xputc to
1781 * actually output the character. We're faster doing
1784 if (iflags
.use_menu_color
1785 && (menucolr
= get_menu_coloring(curr
->str
, &color
,
1787 term_start_attr(attr
);
1789 if (color
!= NO_COLOR
)
1790 term_start_color(color
);
1793 term_start_attr(curr
->attr
);
1794 for (n
= 0, cp
= curr
->str
;
1797 && (int) ++ttyDisplay
->curx
< (int) ttyDisplay
->cols
;
1801 && (int) ttyDisplay
->curx
< (int) ttyDisplay
->cols
;
1802 cp
++, n
++, ttyDisplay
->curx
++)
1804 if (n
== 2 && curr
->identifier
.a_void
!= 0
1805 && curr
->selected
) {
1806 if (curr
->count
== -1L)
1807 (void) putchar('+'); /* all selected */
1809 (void) putchar('#'); /* count selected */
1811 (void) putchar(*cp
);
1812 if (iflags
.use_menu_color
&& menucolr
) {
1814 if (color
!= NO_COLOR
)
1817 term_end_attr(attr
);
1819 term_end_attr(curr
->attr
);
1827 /* remember how many explicit menu choices there are */
1828 resp_len
= (int) strlen(resp
);
1830 /* corner window - clear extra lines from last page */
1832 for (n
= page_lines
+ 1; n
< cw
->maxrow
; n
++) {
1833 tty_curs(window
, 1, n
);
1838 /* set extra chars.. */
1839 Strcat(resp
, default_menu_cmds
);
1840 Strcat(resp
, "0123456789\033\n\r"); /* counts, quit */
1841 Strcat(resp
, gacc
); /* group accelerators */
1842 Strcat(resp
, mapped_menu_cmds
);
1845 Sprintf(cw
->morestr
, "(%d of %d)", curr_page
+ 1,
1848 Strcpy(cw
->morestr
, msave
);
1850 Strcpy(cw
->morestr
, defmorestr
);
1852 tty_curs(window
, 1, page_lines
);
1856 /* just put the cursor back... */
1857 tty_curs(window
, (int) strlen(cw
->morestr
) + 2, page_lines
);
1858 xwaitforspace(resp
);
1861 really_morc
= morc
; /* (only used with MENU_EXPLICIT_CHOICE */
1862 if ((rp
= index(resp
, morc
)) != 0 && rp
< resp
+ resp_len
)
1863 /* explicit menu selection; don't override it if it also
1864 happens to match a mapped menu command (such as ':' to
1865 look inside a container vs ':' to search) */
1866 morc
= MENU_EXPLICIT_CHOICE
;
1868 morc
= map_menu_cmd(morc
);
1872 /* special case: '0' is also the default ball class */
1873 if (!counting
&& index(gacc
, morc
))
1875 /* fall through to count the zero */
1885 count
= (count
* 10L) + (long) (morc
- '0');
1887 * It is debatable whether we should allow 0 to
1888 * start a count. There is no difference if the
1889 * item is selected. If not selected, then
1892 * count starting zero: "zero b's"
1893 * ignore starting zero: "select b"
1895 * At present I don't know which is better.
1897 if (count
!= 0L) { /* ignore leading zeros */
1899 reset_count
= FALSE
;
1902 case '\033': /* cancel - from counting or loop */
1904 /* deselect everything */
1905 for (curr
= cw
->mlist
; curr
; curr
= curr
->next
) {
1906 curr
->selected
= FALSE
;
1909 cw
->flags
|= WIN_CANCELLED
;
1912 /* else only stop count */
1914 case '\0': /* finished (commit) */
1917 /* only finished if we are actually picking something */
1918 if (cw
->how
!= PICK_NONE
) {
1922 /* else fall through */
1923 case MENU_NEXT_PAGE
:
1924 if (cw
->npages
> 0 && curr_page
!= cw
->npages
- 1) {
1928 finished
= TRUE
; /* questionable behavior */
1930 case MENU_PREVIOUS_PAGE
:
1931 if (cw
->npages
> 0 && curr_page
!= 0) {
1936 case MENU_FIRST_PAGE
:
1937 if (cw
->npages
> 0 && curr_page
!= 0) {
1942 case MENU_LAST_PAGE
:
1943 if (cw
->npages
> 0 && curr_page
!= cw
->npages
- 1) {
1945 curr_page
= cw
->npages
- 1;
1948 case MENU_SELECT_PAGE
:
1949 if (cw
->how
== PICK_ANY
)
1950 set_all_on_page(window
, page_start
, page_end
);
1952 case MENU_UNSELECT_PAGE
:
1953 unset_all_on_page(window
, page_start
, page_end
);
1955 case MENU_INVERT_PAGE
:
1956 if (cw
->how
== PICK_ANY
)
1957 invert_all_on_page(window
, page_start
, page_end
, 0);
1959 case MENU_SELECT_ALL
:
1960 if (cw
->how
== PICK_ANY
) {
1961 set_all_on_page(window
, page_start
, page_end
);
1963 for (curr
= cw
->mlist
; curr
; curr
= curr
->next
)
1964 if (curr
->identifier
.a_void
&& !curr
->selected
)
1965 curr
->selected
= TRUE
;
1968 case MENU_UNSELECT_ALL
:
1969 unset_all_on_page(window
, page_start
, page_end
);
1970 /* unset the rest */
1971 for (curr
= cw
->mlist
; curr
; curr
= curr
->next
)
1972 if (curr
->identifier
.a_void
&& curr
->selected
) {
1973 curr
->selected
= FALSE
;
1977 case MENU_INVERT_ALL
:
1978 if (cw
->how
== PICK_ANY
)
1979 invert_all(window
, page_start
, page_end
, 0);
1982 if (cw
->how
== PICK_NONE
) {
1986 char searchbuf
[BUFSZ
+ 2], tmpbuf
[BUFSZ
];
1987 boolean on_curr_page
= FALSE
;
1990 tty_getlin("Search for:", tmpbuf
);
1991 if (!tmpbuf
[0] || tmpbuf
[0] == '\033')
1993 Sprintf(searchbuf
, "*%s*", tmpbuf
);
1995 for (curr
= cw
->mlist
; curr
; curr
= curr
->next
) {
1998 if (curr
== page_start
)
1999 on_curr_page
= TRUE
;
2000 else if (curr
== page_end
)
2001 on_curr_page
= FALSE
;
2002 if (curr
->identifier
.a_void
2003 && pmatchi(searchbuf
, curr
->str
)) {
2004 toggle_menu_curr(window
, curr
, lineno
, on_curr_page
,
2006 if (cw
->how
== PICK_ONE
) {
2014 case MENU_EXPLICIT_CHOICE
:
2018 if (cw
->how
== PICK_NONE
|| !index(resp
, morc
)) {
2019 /* unacceptable input received */
2022 } else if (index(gacc
, morc
)) {
2024 /* group accelerator; for the PICK_ONE case, we know that
2025 it matches exactly one item in order to be in gacc[] */
2026 invert_all(window
, page_start
, page_end
, morc
);
2027 if (cw
->how
== PICK_ONE
)
2031 /* find, toggle, and possibly update */
2032 for (n
= 0, curr
= page_start
; curr
!= page_end
;
2033 n
++, curr
= curr
->next
)
2034 if (morc
== curr
->selector
) {
2035 toggle_menu_curr(window
, curr
, n
, TRUE
, counting
, count
);
2036 if (cw
->how
== PICK_ONE
)
2038 break; /* from `for' loop */
2044 cw
->morestr
= msave
;
2045 free((genericptr_t
) morestr
);
2049 process_text_window(window
, cw
)
2056 for (n
= 0, i
= 0; i
< cw
->maxrow
; i
++) {
2057 if (!cw
->offx
&& (n
+ cw
->offy
== ttyDisplay
->rows
- 1)) {
2058 tty_curs(window
, 1, n
);
2060 dmore(cw
, quitchars
);
2061 if (morc
== '\033') {
2062 cw
->flags
|= WIN_CANCELLED
;
2066 tty_curs(window
, 1, 0);
2072 tty_curs(window
, 1, n
++);
2080 attr
= cw
->data
[i
][0] - 1;
2082 (void) putchar(' ');
2085 term_start_attr(attr
);
2086 for (cp
= &cw
->data
[i
][1];
2088 *cp
&& (int) ++ttyDisplay
->curx
< (int) ttyDisplay
->cols
;
2091 *cp
&& (int) ttyDisplay
->curx
< (int) ttyDisplay
->cols
;
2092 cp
++, ttyDisplay
->curx
++)
2094 (void) putchar(*cp
);
2095 term_end_attr(attr
);
2098 if (i
== cw
->maxrow
) {
2100 if (cw
->type
== NHW_TEXT
) {
2101 tty_curs(BASE_WINDOW
, 1, (int) ttyDisplay
->cury
+ 1);
2105 tty_curs(BASE_WINDOW
, (int) cw
->offx
+ 1,
2106 (cw
->type
== NHW_TEXT
) ? (int) ttyDisplay
->rows
- 1 : n
);
2108 dmore(cw
, quitchars
);
2110 cw
->flags
|= WIN_CANCELLED
;
2116 tty_display_nhwindow(window
, blocking
)
2118 boolean blocking
; /* with ttys, all windows are blocking */
2120 register struct WinDesc
*cw
= 0;
2123 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0)
2124 panic(winpanicstr
, window
);
2125 if (cw
->flags
& WIN_CANCELLED
)
2127 ttyDisplay
->lastwin
= window
;
2128 ttyDisplay
->rawprint
= 0;
2130 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
2134 if (ttyDisplay
->toplin
== 1) {
2136 ttyDisplay
->toplin
= 1; /* more resets this */
2137 tty_clear_nhwindow(window
);
2139 ttyDisplay
->toplin
= 0;
2140 cw
->curx
= cw
->cury
= 0;
2142 iflags
.window_inited
= TRUE
;
2147 if (!ttyDisplay
->toplin
)
2148 ttyDisplay
->toplin
= 1;
2149 tty_display_nhwindow(WIN_MESSAGE
, TRUE
);
2153 (void) fflush(stdout
);
2156 cw
->maxcol
= ttyDisplay
->cols
; /* force full-screen mode */
2160 /* cw->maxcol is a long, but its value is constrained to
2161 be <= ttyDisplay->cols, so is sure to fit within a short */
2162 s_maxcol
= (short) cw
->maxcol
;
2164 cw
->offx
= (cw
->type
== NHW_TEXT
)
2166 : min(min(82, ttyDisplay
->cols
/ 2),
2167 ttyDisplay
->cols
- s_maxcol
- 1);
2169 /* avoid converting to uchar before calculations are finished */
2170 cw
->offx
= (uchar
) max((int) 10,
2171 (int) (ttyDisplay
->cols
- s_maxcol
- 1));
2175 if (cw
->type
== NHW_MENU
)
2177 if (ttyDisplay
->toplin
== 1)
2178 tty_display_nhwindow(WIN_MESSAGE
, TRUE
);
2180 if (cw
->maxrow
>= (int) ttyDisplay
->rows
2181 || !iflags
.menu_overlay
)
2183 if (cw
->offx
== 10 || cw
->maxrow
>= (int) ttyDisplay
->rows
2184 || !iflags
.menu_overlay
)
2188 if (cw
->offy
|| iflags
.menu_overlay
) {
2189 tty_curs(window
, 1, 0);
2193 ttyDisplay
->toplin
= 0;
2195 if (WIN_MESSAGE
!= WIN_ERR
)
2196 tty_clear_nhwindow(WIN_MESSAGE
);
2199 if (cw
->data
|| !cw
->maxrow
)
2200 process_text_window(window
, cw
);
2202 process_menu_window(window
, cw
);
2209 tty_dismiss_nhwindow(window
)
2212 register struct WinDesc
*cw
= 0;
2214 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0)
2215 panic(winpanicstr
, window
);
2217 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
2221 if (ttyDisplay
->toplin
)
2222 tty_display_nhwindow(WIN_MESSAGE
, TRUE
);
2228 * these should only get dismissed when the game is going away
2231 tty_curs(BASE_WINDOW
, 1, (int) ttyDisplay
->rows
- 1);
2237 if (iflags
.window_inited
) {
2238 /* otherwise dismissing the text endwin after other windows
2239 * are dismissed tries to redraw the map and panics. since
2240 * the whole reason for dismissing the other windows was to
2241 * leave the ending window on the screen, we don't want to
2244 erase_menu_or_text(window
, cw
, FALSE
);
2254 tty_destroy_nhwindow(window
)
2257 register struct WinDesc
*cw
= 0;
2259 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0)
2260 panic(winpanicstr
, window
);
2263 tty_dismiss_nhwindow(window
);
2264 if (cw
->type
== NHW_MESSAGE
)
2265 iflags
.window_inited
= 0;
2266 if (cw
->type
== NHW_MAP
)
2269 free_window_info(cw
, TRUE
);
2270 free((genericptr_t
) cw
);
2275 tty_curs(window
, x
, y
)
2277 register int x
, y
; /* not xchar: perhaps xchar is unsigned and
2278 curx-x would be unsigned as well */
2280 struct WinDesc
*cw
= 0;
2281 int cx
= ttyDisplay
->curx
;
2282 int cy
= ttyDisplay
->cury
;
2284 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0)
2285 panic(winpanicstr
, window
);
2286 ttyDisplay
->lastwin
= window
;
2288 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
2290 #if defined(USE_TILES) && defined(MSDOS)
2291 adjust_cursor_flags(cw
);
2293 cw
->curx
= --x
; /* column 0 is never used */
2296 if (x
< 0 || y
< 0 || y
>= cw
->rows
|| x
> cw
->cols
) {
2297 const char *s
= "[unknown type]";
2300 s
= "[topl window]";
2303 s
= "[status window]";
2309 s
= "[corner window]";
2312 s
= "[text window]";
2315 s
= "[base window]";
2318 debugpline4("bad curs positioning win %d %s (%d,%d)", window
, s
, x
,
2327 if (clipping
&& window
== WIN_MAP
) {
2333 if (y
== cy
&& x
== cx
)
2336 if (cw
->type
== NHW_MAP
)
2340 if (!nh_ND
&& (cx
!= x
|| x
<= 3)) { /* Extremely primitive */
2341 cmov(x
, y
); /* bunker!wtm */
2350 if (cy
<= 3 && cx
<= 3) {
2353 } else if ((x
<= 3 && cy
<= 3) || (!nh_CM
&& x
< cx
)) {
2354 (void) putchar('\r');
2355 ttyDisplay
->curx
= 0;
2357 } else if (!nh_CM
) {
2363 ttyDisplay
->curx
= x
;
2364 ttyDisplay
->cury
= y
;
2368 tty_putsym(window
, x
, y
, ch
)
2373 register struct WinDesc
*cw
= 0;
2375 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0)
2376 panic(winpanicstr
, window
);
2378 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
2384 tty_curs(window
, x
, y
);
2392 impossible("Can't putsym to window type %d", cw
->type
);
2397 STATIC_OVL
const char *
2401 static char cbuf
[BUFSZ
];
2402 /* compress in case line too long */
2403 if ((int) strlen(str
) >= CO
) {
2404 register const char *bp0
= str
;
2405 register char *bp1
= cbuf
;
2409 if (*bp0
!= ' ' || bp0
[1] != ' ')
2411 if (*bp0
!= ' ' || bp0
[1] != ' ' || bp0
[2] != ' ')
2421 tty_putstr(window
, attr
, str
)
2426 register struct WinDesc
*cw
= 0;
2428 register const char *nb
;
2429 register long i
, j
, n0
;
2431 /* Assume there's a real problem if the window is missing --
2432 * probably a panic message
2434 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0) {
2439 if (str
== (const char *) 0
2440 || ((cw
->flags
& WIN_CANCELLED
) && (cw
->type
!= NHW_MESSAGE
)))
2442 if (cw
->type
!= NHW_MESSAGE
)
2443 str
= compress_str(str
);
2445 ttyDisplay
->lastwin
= window
;
2447 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
2451 /* really do this later */
2452 #if defined(USER_SOUNDS) && defined(WIN32CON)
2453 play_sound_for_message(str
);
2459 ob
= &cw
->data
[cw
->cury
][j
= cw
->curx
];
2462 if (!cw
->cury
&& (int) strlen(str
) >= CO
) {
2463 /* the characters before "St:" are unnecessary */
2464 nb
= index(str
, ':');
2465 if (nb
&& nb
> str
+ 2)
2469 for (i
= cw
->curx
+ 1, n0
= cw
->cols
; i
< n0
; i
++, nb
++) {
2471 if (*ob
|| context
.botlx
) {
2472 /* last char printed may be in middle of line */
2473 tty_curs(WIN_STATUS
, i
, cw
->cury
);
2479 tty_putsym(WIN_STATUS
, i
, cw
->cury
, *nb
);
2484 (void) strncpy(&cw
->data
[cw
->cury
][j
], str
, cw
->cols
- j
- 1);
2485 cw
->data
[cw
->cury
][cw
->cols
- 1] = '\0'; /* null terminate */
2486 #ifdef STATUS_VIA_WINDOWPORT
2487 if (!iflags
.use_status_hilites
) {
2489 cw
->cury
= (cw
->cury
+ 1) % 2;
2491 #ifdef STATUS_VIA_WINDOWPORT
2496 tty_curs(window
, cw
->curx
+ 1, cw
->cury
);
2497 term_start_attr(attr
);
2498 while (*str
&& (int) ttyDisplay
->curx
< (int) ttyDisplay
->cols
- 1) {
2499 (void) putchar(*str
);
2505 term_end_attr(attr
);
2508 tty_curs(window
, cw
->curx
+ 1, cw
->cury
);
2509 term_start_attr(attr
);
2511 if ((int) ttyDisplay
->curx
>= (int) ttyDisplay
->cols
- 1) {
2514 tty_curs(window
, cw
->curx
+ 1, cw
->cury
);
2516 (void) putchar(*str
);
2522 term_end_attr(attr
);
2527 if (cw
->type
== NHW_TEXT
2528 && (cw
->cury
+ cw
->offy
) == ttyDisplay
->rows
- 1)
2530 if (cw
->type
== NHW_TEXT
&& cw
->cury
== ttyDisplay
->rows
- 1)
2533 /* not a menu, so save memory and output 1 page at a time */
2534 cw
->maxcol
= ttyDisplay
->cols
; /* force full-screen mode */
2535 tty_display_nhwindow(window
, TRUE
);
2536 for (i
= 0; i
< cw
->maxrow
; i
++)
2538 free((genericptr_t
) cw
->data
[i
]);
2541 cw
->maxrow
= cw
->cury
= 0;
2543 /* always grows one at a time, but alloc 12 at a time */
2544 if (cw
->cury
>= cw
->rows
) {
2548 tmp
= (char **) alloc(sizeof(char *) * (unsigned) cw
->rows
);
2549 for (i
= 0; i
< cw
->maxrow
; i
++)
2550 tmp
[i
] = cw
->data
[i
];
2552 free((genericptr_t
) cw
->data
);
2555 for (i
= cw
->maxrow
; i
< cw
->rows
; i
++)
2558 if (cw
->data
[cw
->cury
])
2559 free((genericptr_t
) cw
->data
[cw
->cury
]);
2560 n0
= strlen(str
) + 1;
2561 ob
= cw
->data
[cw
->cury
] = (char *) alloc((unsigned) n0
+ 1);
2562 *ob
++ = (char) (attr
+ 1); /* avoid nuls, for convenience */
2565 if (n0
> cw
->maxcol
)
2567 if (++cw
->cury
> cw
->maxrow
)
2568 cw
->maxrow
= cw
->cury
;
2570 /* attempt to break the line */
2571 for (i
= CO
- 1; i
&& str
[i
] != ' ' && str
[i
] != '\n';)
2574 cw
->data
[cw
->cury
- 1][++i
] = '\0';
2575 tty_putstr(window
, attr
, &str
[i
]);
2583 tty_display_file(fname
, complain
)
2587 #ifdef DEF_PAGER /* this implies that UNIX is defined */
2589 /* use external pager; this may give security problems */
2590 register int fd
= open(fname
, 0);
2594 pline("Cannot open %s.", fname
);
2600 /* Now that child() does a setuid(getuid()) and a chdir(),
2601 we may not be able to open file fname anymore, so make
2606 raw_printf("Cannot open %s as stdin.", fname
);
2608 (void) execlp(catmore
, "page", (char *) 0);
2610 raw_printf("Cannot exec %s.", catmore
);
2613 sleep(10); /* want to wait_synch() but stdin is gone */
2614 terminate(EXIT_FAILURE
);
2621 #else /* DEF_PAGER */
2627 tty_clear_nhwindow(WIN_MESSAGE
);
2628 f
= dlb_fopen(fname
, "r");
2636 pline("Cannot open \"%s\".", fname
);
2640 winid datawin
= tty_create_nhwindow(NHW_TEXT
);
2641 boolean empty
= TRUE
;
2648 /* attempt to scroll text below map window if there's room */
2649 wins
[datawin
]->offy
= wins
[WIN_STATUS
]->offy
+ 3;
2650 if ((int) wins
[datawin
]->offy
+ 12 > (int) ttyDisplay
->rows
)
2651 wins
[datawin
]->offy
= 0;
2653 while (dlb_fgets(buf
, BUFSZ
, f
)) {
2654 if ((cr
= index(buf
, '\n')) != 0)
2657 if ((cr
= index(buf
, '\r')) != 0)
2660 if (index(buf
, '\t') != 0)
2661 (void) tabexpand(buf
);
2663 tty_putstr(datawin
, 0, buf
);
2664 if (wins
[datawin
]->flags
& WIN_CANCELLED
)
2668 tty_display_nhwindow(datawin
, FALSE
);
2669 tty_destroy_nhwindow(datawin
);
2670 (void) dlb_fclose(f
);
2673 #endif /* DEF_PAGER */
2677 tty_start_menu(window
)
2680 tty_clear_nhwindow(window
);
2686 * Add a menu item to the beginning of the menu list. This list is reversed
2690 tty_add_menu(window
, glyph
, identifier
, ch
, gch
, attr
, str
, preselected
)
2691 winid window
; /* window to use, must be of type NHW_MENU */
2692 int glyph UNUSED
; /* glyph to display with item (not used) */
2693 const anything
*identifier
; /* what to return if selected */
2694 char ch
; /* keyboard accelerator (0 = pick our own) */
2695 char gch
; /* group accelerator (0 = no group) */
2696 int attr
; /* attribute for string (like tty_putstr()) */
2697 const char *str
; /* menu string */
2698 boolean preselected
; /* item is marked as selected */
2700 register struct WinDesc
*cw
= 0;
2701 tty_menu_item
*item
;
2703 char buf
[4 + BUFSZ
];
2705 if (str
== (const char *) 0)
2708 if (window
== WIN_ERR
2709 || (cw
= wins
[window
]) == (struct WinDesc
*) 0
2710 || cw
->type
!= NHW_MENU
)
2711 panic(winpanicstr
, window
);
2714 if (identifier
->a_void
) {
2715 int len
= strlen(str
);
2718 /* We *think* everything's coming in off at most BUFSZ bufs... */
2719 impossible("Menu item too long (%d).", len
);
2722 Sprintf(buf
, "%c - ", ch
? ch
: '?');
2723 (void) strncpy(buf
+ 4, str
, len
);
2724 buf
[4 + len
] = '\0';
2729 item
= (tty_menu_item
*) alloc(sizeof(tty_menu_item
));
2730 item
->identifier
= *identifier
;
2732 item
->selected
= preselected
;
2733 item
->selector
= ch
;
2734 item
->gselector
= gch
;
2736 item
->str
= dupstr(newstr
? newstr
: "");
2738 item
->next
= cw
->mlist
;
2742 /* Invert the given list, can handle NULL as an input. */
2743 STATIC_OVL tty_menu_item
*
2745 tty_menu_item
*curr
;
2747 tty_menu_item
*next
, *head
= 0;
2759 * End a menu in this window, window must a type NHW_MENU. This routine
2760 * processes the string list. We calculate the # of pages, then assign
2761 * keyboard accelerators as needed. Finally we decide on the width and
2762 * height of the window.
2765 tty_end_menu(window
, prompt
)
2766 winid window
; /* menu to use */
2767 const char *prompt
; /* prompt to for menu */
2769 struct WinDesc
*cw
= 0;
2770 tty_menu_item
*curr
;
2775 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0
2776 || cw
->type
!= NHW_MENU
)
2777 panic(winpanicstr
, window
);
2779 /* Reverse the list so that items are in correct order. */
2780 cw
->mlist
= reverse(cw
->mlist
);
2782 /* Put the prompt at the beginning of the menu. */
2786 any
= zeroany
; /* not selectable */
2787 tty_add_menu(window
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "",
2789 tty_add_menu(window
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, prompt
,
2793 /* XXX another magic number? 52 */
2794 lmax
= min(52, (int) ttyDisplay
->rows
- 1); /* # lines per page */
2795 cw
->npages
= (cw
->nitems
+ (lmax
- 1)) / lmax
; /* # of pages */
2797 /* make sure page list is large enough */
2798 if (cw
->plist_size
< cw
->npages
+ 1 /*need 1 slot beyond last*/) {
2800 free((genericptr_t
) cw
->plist
);
2801 cw
->plist_size
= cw
->npages
+ 1;
2802 cw
->plist
= (tty_menu_item
**) alloc(cw
->plist_size
2803 * sizeof(tty_menu_item
*));
2806 cw
->cols
= 0; /* cols is set when the win is initialized... (why?) */
2807 menu_ch
= '?'; /* lint suppression */
2808 for (n
= 0, curr
= cw
->mlist
; curr
; n
++, curr
= curr
->next
) {
2809 /* set page boundaries and character accelerators */
2810 if ((n
% lmax
) == 0) {
2812 cw
->plist
[n
/ lmax
] = curr
;
2814 if (curr
->identifier
.a_void
&& !curr
->selector
) {
2815 curr
->str
[0] = curr
->selector
= menu_ch
;
2816 if (menu_ch
++ == 'z')
2820 /* cut off any lines that are too long */
2821 len
= strlen(curr
->str
) + 2; /* extra space at beg & end */
2822 if (len
> (int) ttyDisplay
->cols
) {
2823 curr
->str
[ttyDisplay
->cols
- 2] = 0;
2824 len
= ttyDisplay
->cols
;
2829 cw
->plist
[cw
->npages
] = 0; /* plist terminator */
2832 * If greater than 1 page, morestr is "(x of y) " otherwise, "(end) "
2834 if (cw
->npages
> 1) {
2836 /* produce the largest demo string */
2837 Sprintf(buf
, "(%ld of %ld) ", cw
->npages
, cw
->npages
);
2839 cw
->morestr
= dupstr("");
2841 cw
->morestr
= dupstr("(end) ");
2842 len
= strlen(cw
->morestr
);
2845 if (len
> (int) ttyDisplay
->cols
) {
2846 /* truncate the prompt if it's too long for the screen */
2847 if (cw
->npages
<= 1) /* only str in single page case */
2848 cw
->morestr
[ttyDisplay
->cols
] = 0;
2849 len
= ttyDisplay
->cols
;
2854 cw
->maxcol
= cw
->cols
;
2857 * The number of lines in the first page plus the morestr will be the
2858 * maximum size of the window.
2861 cw
->maxrow
= cw
->rows
= lmax
+ 1;
2863 cw
->maxrow
= cw
->rows
= cw
->nitems
+ 1;
2867 tty_select_menu(window
, how
, menu_list
)
2870 menu_item
**menu_list
;
2872 register struct WinDesc
*cw
= 0;
2873 tty_menu_item
*curr
;
2877 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0
2878 || cw
->type
!= NHW_MENU
)
2879 panic(winpanicstr
, window
);
2881 *menu_list
= (menu_item
*) 0;
2882 cw
->how
= (short) how
;
2884 tty_display_nhwindow(window
, TRUE
);
2885 cancelled
= !!(cw
->flags
& WIN_CANCELLED
);
2886 tty_dismiss_nhwindow(window
); /* does not destroy window data */
2891 for (n
= 0, curr
= cw
->mlist
; curr
; curr
= curr
->next
)
2897 *menu_list
= (menu_item
*) alloc(n
* sizeof(menu_item
));
2898 for (mi
= *menu_list
, curr
= cw
->mlist
; curr
; curr
= curr
->next
)
2899 if (curr
->selected
) {
2900 mi
->item
= curr
->identifier
;
2901 mi
->count
= curr
->count
;
2909 /* special hack for treating top line --More-- as a one item menu */
2911 tty_message_menu(let
, how
, mesg
)
2916 /* "menu" without selection; use ordinary pline, no more() */
2917 if (how
== PICK_NONE
) {
2922 ttyDisplay
->dismiss_more
= let
;
2924 /* barebones pline(); since we're only supposed to be called after
2925 response to a prompt, we'll assume that the display is up to date */
2926 tty_putstr(WIN_MESSAGE
, 0, mesg
);
2927 /* if `mesg' didn't wrap (triggering --More--), force --More-- now */
2928 if (ttyDisplay
->toplin
== 1) {
2930 ttyDisplay
->toplin
= 1; /* more resets this */
2931 tty_clear_nhwindow(WIN_MESSAGE
);
2933 /* normally <ESC> means skip further messages, but in this case
2934 it means cancel the current prompt; any other messages should
2935 continue to be output normally */
2936 wins
[WIN_MESSAGE
]->flags
&= ~WIN_CANCELLED
;
2937 ttyDisplay
->dismiss_more
= 0;
2939 return ((how
== PICK_ONE
&& morc
== let
) || morc
== '\033') ? morc
: '\0';
2943 tty_update_inventory()
2951 (void) fflush(stdout
);
2957 /* we just need to make sure all windows are synch'd */
2958 if (!ttyDisplay
|| ttyDisplay
->rawprint
) {
2961 ttyDisplay
->rawprint
= 0;
2963 tty_display_nhwindow(WIN_MAP
, FALSE
);
2964 if (ttyDisplay
->inmore
) {
2965 addtopl("--More--");
2966 (void) fflush(stdout
);
2967 } else if (ttyDisplay
->inread
> program_state
.gameover
) {
2968 /* this can only happen if we were reading and got interrupted */
2969 ttyDisplay
->toplin
= 3;
2970 /* do this twice; 1st time gets the Quit? message again */
2971 (void) tty_doprev_message();
2972 (void) tty_doprev_message();
2974 (void) fflush(stdout
);
2980 docorner(xmin
, ymax
)
2981 register int xmin
, ymax
;
2984 register struct WinDesc
*cw
= wins
[WIN_MAP
];
2986 if (u
.uswallow
) { /* Can be done more efficiently */
2991 #if defined(SIGWINCH) && defined(CLIPPING)
2993 ymax
= LI
; /* can happen if window gets smaller */
2995 for (y
= 0; y
< ymax
; y
++) {
2996 tty_curs(BASE_WINDOW
, xmin
, y
); /* move cursor */
2997 cl_end(); /* clear to end of line */
2999 if (y
< (int) cw
->offy
|| y
+ clipy
> ROWNO
)
3000 continue; /* only refresh board */
3001 #if defined(USE_TILES) && defined(MSDOS)
3002 if (iflags
.tile_view
)
3003 row_refresh((xmin
/ 2) + clipx
- ((int) cw
->offx
/ 2), COLNO
- 1,
3004 y
+ clipy
- (int) cw
->offy
);
3007 row_refresh(xmin
+ clipx
- (int) cw
->offx
, COLNO
- 1,
3008 y
+ clipy
- (int) cw
->offy
);
3010 if (y
< cw
->offy
|| y
> ROWNO
)
3011 continue; /* only refresh board */
3012 row_refresh(xmin
- (int) cw
->offx
, COLNO
- 1, y
- (int) cw
->offy
);
3017 if (ymax
>= (int) wins
[WIN_STATUS
]->offy
) {
3018 /* we have wrecked the bottom line */
3027 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3034 if (ttyDisplay
->color
!= NO_COLOR
) {
3036 ttyDisplay
->color
= NO_COLOR
;
3046 register char ch
= (char) in_ch
;
3048 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3049 if (SYMHANDLING(H_IBM
) || iflags
.eight_bit_tty
) {
3050 /* IBM-compatible displays don't need other stuff */
3052 } else if (ch
& 0x80) {
3053 if (!GFlag
|| HE_resets_AS
) {
3057 (void) putchar((ch
^ 0x80)); /* Strip 8th bit */
3069 #endif /* ASCIIGRAPH && !NO_TERMS */
3086 tty_cliparound(x
, y
)
3089 extern boolean restoring
;
3090 int oldx
= clipx
, oldy
= clipy
;
3094 if (x
< clipx
+ 5) {
3095 clipx
= max(0, x
- 20);
3096 clipxmax
= clipx
+ CO
;
3097 } else if (x
> clipxmax
- 5) {
3098 clipxmax
= min(COLNO
, clipxmax
+ 20);
3099 clipx
= clipxmax
- CO
;
3101 if (y
< clipy
+ 2) {
3102 clipy
= max(0, y
- (clipymax
- clipy
) / 2);
3103 clipymax
= clipy
+ (LI
- 3);
3104 } else if (y
> clipymax
- 2) {
3105 clipymax
= min(ROWNO
, clipymax
+ (clipymax
- clipy
) / 2);
3106 clipy
= clipymax
- (LI
- 3);
3108 if (clipx
!= oldx
|| clipy
!= oldy
) {
3109 if (on_level(&u
.uz0
, &u
.uz
) && !restoring
)
3113 #endif /* CLIPPING */
3118 * Print the glyph to the output device. Don't flush the output device.
3120 * Since this is only called from show_glyph(), it is assumed that the
3121 * position and glyph are always correct (checked there)!
3125 tty_print_glyph(window
, x
, y
, glyph
, bkglyph
)
3132 boolean reverse_on
= FALSE
;
3138 if (x
<= clipx
|| y
< clipy
|| x
>= clipxmax
|| y
>= clipymax
)
3142 /* map glyph to character and color */
3143 (void) mapglyph(glyph
, &ch
, &color
, &special
, x
, y
);
3145 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
3147 /* Move the cursor. */
3148 tty_curs(window
, x
, y
);
3150 print_vt_code3(AVTC_GLYPH_START
, glyph2tile
[glyph
], special
);
3153 if (ul_hack
&& ch
== '_') { /* non-destructive underscore */
3154 (void) putchar((char) ' ');
3160 if (color
!= ttyDisplay
->color
) {
3161 if (ttyDisplay
->color
!= NO_COLOR
)
3163 ttyDisplay
->color
= color
;
3164 if (color
!= NO_COLOR
)
3165 term_start_color(color
);
3167 #endif /* TEXTCOLOR */
3169 /* must be after color check; term_end_color may turn off inverse too */
3170 if (((special
& MG_PET
) && iflags
.hilite_pet
)
3171 || ((special
& MG_OBJPILE
) && iflags
.hilite_pile
)
3172 || ((special
& MG_DETECT
) && iflags
.use_inverse
)) {
3173 term_start_attr(ATR_INVERSE
);
3177 #if defined(USE_TILES) && defined(MSDOS)
3178 if (iflags
.grmode
&& iflags
.tile_view
)
3179 xputg(glyph
, ch
, special
);
3182 g_putch(ch
); /* print the character */
3185 term_end_attr(ATR_INVERSE
);
3187 /* turn off color as well, ATR_INVERSE may have done this already */
3188 if (ttyDisplay
->color
!= NO_COLOR
) {
3190 ttyDisplay
->color
= NO_COLOR
;
3195 print_vt_code1(AVTC_GLYPH_END
);
3197 wins
[window
]->curx
++; /* one character over */
3198 ttyDisplay
->curx
++; /* the real cursor moved too */
3206 ttyDisplay
->rawprint
++;
3207 print_vt_code2(AVTC_SELECT_WINDOW
, NHW_BASE
);
3208 #if defined(MICRO) || defined(WIN32CON)
3212 (void) fflush(stdout
);
3217 tty_raw_print_bold(str
)
3221 ttyDisplay
->rawprint
++;
3222 print_vt_code2(AVTC_SELECT_WINDOW
, NHW_BASE
);
3223 term_start_raw_bold();
3224 #if defined(MICRO) || defined(WIN32CON)
3227 (void) fputs(str
, stdout
);
3229 term_end_raw_bold();
3230 #if defined(MICRO) || defined(WIN32CON)
3234 (void) fflush(stdout
);
3243 /* kludge alert: Some Unix variants return funny values if getc()
3244 * is called, interrupted, and then called again. There
3245 * is non-reentrant code in the internal _filbuf() routine, called by
3248 static volatile int nesting
= 0;
3252 print_vt_code1(AVTC_INLINE_SYNC
);
3253 (void) fflush(stdout
);
3254 /* Note: if raw_print() and wait_synch() get called to report terminal
3255 * initialization problems, then wins[] and ttyDisplay might not be
3256 * available yet. Such problems will probably be fatal before we get
3257 * here, but validate those pointers just in case...
3259 if (WIN_MESSAGE
!= WIN_ERR
&& wins
[WIN_MESSAGE
])
3260 wins
[WIN_MESSAGE
]->flags
&= ~WIN_STOP
;
3262 i
= (++nesting
== 1) ? tgetch()
3263 : (read(fileno(stdin
), (genericptr_t
) &nestbuf
, 1)
3264 == 1) ? (int) nestbuf
: EOF
;
3270 i
= '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
3272 i
= '\033'; /* same for EOF */
3273 if (ttyDisplay
&& ttyDisplay
->toplin
== 1)
3274 ttyDisplay
->toplin
= 2;
3275 #ifdef TTY_TILES_ESCCODES
3277 /* hack to force output of the window select code */
3278 int tmp
= vt_tile_current_window
;
3279 vt_tile_current_window
++;
3280 print_vt_code2(AVTC_SELECT_WINDOW
, tmp
);
3282 #endif /* TTY_TILES_ESCCODES */
3287 * return a key, or 0, in which case a mouse button was pressed
3288 * mouse events should be returned as character postitions in the map window.
3289 * Since normal tty's don't have mice, just return a key.
3293 tty_nh_poskey(x
, y
, mod
)
3296 #if defined(WIN32CON)
3298 (void) fflush(stdout
);
3299 /* Note: if raw_print() and wait_synch() get called to report terminal
3300 * initialization problems, then wins[] and ttyDisplay might not be
3301 * available yet. Such problems will probably be fatal before we get
3302 * here, but validate those pointers just in case...
3304 if (WIN_MESSAGE
!= WIN_ERR
&& wins
[WIN_MESSAGE
])
3305 wins
[WIN_MESSAGE
]->flags
&= ~WIN_STOP
;
3306 i
= ntposkey(x
, y
, mod
);
3307 if (!i
&& mod
&& (*mod
== 0 || *mod
== EOF
))
3308 i
= '\033'; /* map NUL or EOF to ESC, nethack doesn't expect either */
3309 if (ttyDisplay
&& ttyDisplay
->toplin
== 1)
3310 ttyDisplay
->toplin
= 2;
3312 #else /* !WIN32CON */
3317 return tty_nhgetch();
3318 #endif /* ?WIN32CON */
3327 #if defined(WIN32CON)
3328 if (!strncmpi(windowprocs
.name
, "tty", 3))
3336 tty_update_positionbar(posbar
)
3340 video_update_positionbar(posbar
);
3345 #ifdef STATUS_VIA_WINDOWPORT
3347 * The following data structures come from the genl_ routines in
3348 * src/windows.c and as such are considered to be on the window-port
3349 * "side" of things, rather than the NetHack-core "side" of things.
3352 extern const char *status_fieldnm
[MAXBLSTATS
];
3353 extern const char *status_fieldfmt
[MAXBLSTATS
];
3354 extern char *status_vals
[MAXBLSTATS
];
3355 extern boolean status_activefields
[MAXBLSTATS
];
3356 extern winid WIN_STATUS
;
3358 #ifdef STATUS_HILITES
3359 typedef struct hilite_data_struct
{
3366 static hilite_data_t tty_status_hilites
[MAXBLSTATS
];
3367 static int tty_status_colors
[MAXBLSTATS
];
3369 struct color_option
{
3374 static void FDECL(start_color_option
, (struct color_option
));
3375 static void FDECL(end_color_option
, (struct color_option
));
3376 static void FDECL(apply_color_option
, (struct color_option
, const char *));
3377 static void FDECL(add_colored_text
, (const char *, char *));
3385 /* let genl_status_init do most of the initialization */
3388 for (i
= 0; i
< MAXBLSTATS
; ++i
) {
3389 #ifdef STATUS_HILITES
3390 tty_status_colors
[i
] = NO_COLOR
; /* no color */
3391 tty_status_hilites
[i
].thresholdtype
= 0;
3392 tty_status_hilites
[i
].behavior
= BL_TH_NONE
;
3393 tty_status_hilites
[i
].under
= BL_HILITE_NONE
;
3394 tty_status_hilites
[i
].over
= BL_HILITE_NONE
;
3395 #endif /* STATUS_HILITES */
3401 * -- update the value of a status field.
3402 * -- the fldindex identifies which field is changing and
3403 * is an integer index value from botl.h
3404 * -- fldindex could be any one of the following from botl.h:
3405 * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3406 * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
3407 * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
3408 * BL_LEVELDESC, BL_EXP, BL_CONDITION
3409 * -- fldindex could also be BL_FLUSH (-1), which is not really
3410 * a field index, but is a special trigger to tell the
3411 * windowport that it should redisplay all its status fields,
3412 * even if no changes have been presented to it.
3413 * -- ptr is usually a "char *", unless fldindex is BL_CONDITION.
3414 * If fldindex is BL_CONDITION, then ptr is a long value with
3415 * any or none of the following bits set (from botl.h):
3416 * BL_MASK_STONE 0x00000001L
3417 * BL_MASK_SLIME 0x00000002L
3418 * BL_MASK_STRNGL 0x00000004L
3419 * BL_MASK_FOODPOIS 0x00000008L
3420 * BL_MASK_TERMILL 0x00000010L
3421 * BL_MASK_BLIND 0x00000020L
3422 * BL_MASK_DEAF 0x00000040L
3423 * BL_MASK_STUN 0x00000080L
3424 * BL_MASK_CONF 0x00000100L
3425 * BL_MASK_HALLU 0x00000200L
3426 * BL_MASK_LEV 0x00000400L
3427 * BL_MASK_FLY 0x00000800L
3428 * BL_MASK_RIDE 0x00001000L
3429 * -- The value passed for BL_GOLD includes an encoded leading
3430 * symbol for GOLD "\GXXXXNNNN:nnn". If the window port needs to use
3431 * the textual gold amount without the leading "$:" the port will
3432 * have to skip past ':' in the passed "ptr" for the BL_GOLD case.
3435 tty_status_update(fldidx
, ptr
, chg
, percent
)
3436 int fldidx
, chg
, percent
;
3439 long cond
, *condptr
= (long *) ptr
;
3441 char *text
= (char *) ptr
;
3442 /* Mapping BL attributes to tty attributes
3443 * BL_HILITE_NONE -1 + 3 = 2 (statusattr[2])
3444 * BL_HILITE_INVERSE -2 + 3 = 1 (statusattr[1])
3445 * BL_HILITE_BOLD -3 + 3 = 0 (statusattr[0])
3448 static boolean beenhere
= FALSE
;
3449 enum statusfields fieldorder
[2][15] = {
3450 { BL_TITLE
, BL_STR
, BL_DX
, BL_CO
, BL_IN
, BL_WI
, BL_CH
, BL_ALIGN
,
3451 BL_SCORE
, BL_FLUSH
, BL_FLUSH
, BL_FLUSH
, BL_FLUSH
, BL_FLUSH
,
3453 { BL_LEVELDESC
, BL_GOLD
, BL_HP
, BL_HPMAX
, BL_ENE
, BL_ENEMAX
,
3454 BL_AC
, BL_XP
, BL_EXP
, BL_HD
, BL_TIME
, BL_HUNGER
,
3455 BL_CAP
, BL_CONDITION
, BL_FLUSH
}
3457 #ifdef STATUS_HILITES
3458 static int statusattr
[] = { ATR_BOLD
, ATR_INVERSE
, ATR_NONE
};
3465 if (fldidx
!= BL_FLUSH
) {
3466 if (!status_activefields
[fldidx
])
3471 *status_vals
[fldidx
] = '\0';
3472 if (cond
& BL_MASK_STONE
)
3473 Strcat(status_vals
[fldidx
], " Stone");
3474 if (cond
& BL_MASK_SLIME
)
3475 Strcat(status_vals
[fldidx
], " Slime");
3476 if (cond
& BL_MASK_STRNGL
)
3477 Strcat(status_vals
[fldidx
], " Strngl");
3478 if (cond
& BL_MASK_FOODPOIS
)
3479 Strcat(status_vals
[fldidx
], " FoodPois");
3480 if (cond
& BL_MASK_TERMILL
)
3481 Strcat(status_vals
[fldidx
], " TermIll");
3482 if (cond
& BL_MASK_BLIND
)
3483 Strcat(status_vals
[fldidx
], " Blind");
3484 if (cond
& BL_MASK_DEAF
)
3485 Strcat(status_vals
[fldidx
], " Deaf");
3486 if (cond
& BL_MASK_STUN
)
3487 Strcat(status_vals
[fldidx
], " Stun");
3488 if (cond
& BL_MASK_CONF
)
3489 Strcat(status_vals
[fldidx
], " Conf");
3490 if (cond
& BL_MASK_HALLU
)
3491 Strcat(status_vals
[fldidx
], " Hallu");
3492 if (cond
& BL_MASK_LEV
)
3493 Strcat(status_vals
[fldidx
], " Lev");
3494 if (cond
& BL_MASK_FLY
)
3495 Strcat(status_vals
[fldidx
], " Fly");
3496 if (cond
& BL_MASK_RIDE
)
3497 Strcat(status_vals
[fldidx
], " Ride");
3502 Sprintf(status_vals
[fldidx
],
3503 status_fieldfmt
[fldidx
] ? status_fieldfmt
[fldidx
] : "%s",
3508 #ifdef STATUS_HILITES
3509 switch (tty_status_hilites
[fldidx
].behavior
) {
3511 tty_status_colors
[fldidx
] = NO_COLOR
;
3515 tty_status_colors
[fldidx
] = tty_status_hilites
[fldidx
].over
;
3517 tty_status_colors
[fldidx
] = tty_status_hilites
[fldidx
].under
;
3519 tty_status_colors
[fldidx
] = NO_COLOR
;
3521 case BL_TH_VAL_PERCENTAGE
: {
3524 if (tty_status_hilites
[fldidx
].thresholdtype
!= ANY_INT
) {
3526 "tty_status_update: unsupported percentage threshold type %d",
3527 tty_status_hilites
[fldidx
].thresholdtype
);
3529 pct_th
= tty_status_hilites
[fldidx
].threshold
.a_int
;
3530 tty_status_colors
[fldidx
] = (percent
>= pct_th
)
3531 ? tty_status_hilites
[fldidx
].over
3532 : tty_status_hilites
[fldidx
].under
;
3536 case BL_TH_VAL_ABSOLUTE
: {
3538 int o
= tty_status_hilites
[fldidx
].over
;
3539 int u
= tty_status_hilites
[fldidx
].under
;
3540 anything
*t
= &tty_status_hilites
[fldidx
].threshold
;
3542 switch (tty_status_hilites
[fldidx
].thresholdtype
) {
3544 c
= (value
>= t
->a_long
) ? o
: u
;
3547 c
= (value
>= t
->a_int
) ? o
: u
;
3550 c
= ((unsigned long) value
>= t
->a_uint
) ? o
: u
;
3553 c
= ((unsigned long) value
>= t
->a_ulong
) ? o
: u
;
3556 c
= (value
& t
->a_ulong
) ? o
: u
;
3560 "tty_status_update: unsupported absolute threshold type %d\n",
3561 tty_status_hilites
[fldidx
].thresholdtype
);
3564 tty_status_colors
[fldidx
] = c
;
3568 #endif /* STATUS_HILITES */
3571 /* For now, this version copied from the genl_ version currently
3572 * updates everything on the display, everytime
3575 if (!beenhere
|| !iflags
.use_status_hilites
) {
3576 char newbot1
[MAXCO
], newbot2
[MAXCO
];
3579 for (i
= 0; fieldorder
[0][i
] >= 0; ++i
) {
3580 int idx1
= fieldorder
[0][i
];
3582 if (status_activefields
[idx1
])
3583 Strcat(newbot1
, status_vals
[idx1
]);
3586 for (i
= 0; fieldorder
[1][i
] >= 0; ++i
) {
3587 int idx2
= fieldorder
[1][i
];
3589 if (status_activefields
[idx2
])
3590 Strcat(newbot2
, status_vals
[idx2
]);
3593 curs(WIN_STATUS
, 1, 0);
3594 putstr(WIN_STATUS
, 0, newbot1
);
3595 curs(WIN_STATUS
, 1, 1);
3596 putmixed(WIN_STATUS
, 0, newbot2
); /* putmixed() due to GOLD glyph */
3601 curs(WIN_STATUS
, 1, 0);
3602 for (i
= 0; fieldorder
[0][i
] != BL_FLUSH
; ++i
) {
3603 int fldidx1
= fieldorder
[0][i
];
3605 if (status_activefields
[fldidx1
]) {
3606 #ifdef STATUS_HILITES
3607 if (tty_status_colors
[fldidx1
] < 0
3608 && tty_status_colors
[fldidx1
] >= -3) {
3609 /* attribute, not a color */
3610 attridx
= tty_status_colors
[fldidx1
] + 3;
3611 term_start_attr(statusattr
[attridx
]);
3612 putstr(WIN_STATUS
, 0, status_vals
[fldidx1
]);
3613 term_end_attr(statusattr
[attridx
]);
3616 if (tty_status_colors
[fldidx1
] != CLR_MAX
) {
3617 if (tty_status_colors
[fldidx1
] != NO_COLOR
)
3618 term_start_color(tty_status_colors
[fldidx1
]);
3619 putstr(WIN_STATUS
, 0, status_vals
[fldidx1
]);
3620 if (tty_status_colors
[fldidx1
] != NO_COLOR
)
3624 #endif /* STATUS_HILITES */
3625 putstr(WIN_STATUS
, 0, status_vals
[fldidx1
]);
3628 curs(WIN_STATUS
, 1, 1);
3629 for (i
= 0; fieldorder
[1][i
] != BL_FLUSH
; ++i
) {
3630 int fldidx2
= fieldorder
[1][i
];
3632 if (status_activefields
[fldidx2
]) {
3633 #ifdef STATUS_HILITES
3634 if (tty_status_colors
[fldidx2
] < 0
3635 && tty_status_colors
[fldidx2
] >= -3) {
3636 /* attribute, not a color */
3637 attridx
= tty_status_colors
[fldidx2
] + 3;
3638 term_start_attr(statusattr
[attridx
]);
3639 putstr(WIN_STATUS
, 0, status_vals
[fldidx2
]);
3640 term_end_attr(statusattr
[attridx
]);
3643 if (tty_status_colors
[fldidx2
] != CLR_MAX
) {
3644 if (tty_status_colors
[fldidx2
] != NO_COLOR
)
3645 term_start_color(tty_status_colors
[fldidx2
]);
3646 if (fldidx2
== BL_GOLD
) {
3647 /* putmixed() due to GOLD glyph */
3648 putmixed(WIN_STATUS
, 0, status_vals
[fldidx2
]);
3650 putstr(WIN_STATUS
, 0, status_vals
[fldidx2
]);
3652 if (tty_status_colors
[fldidx2
] != NO_COLOR
)
3656 #endif /* STATUS_HILITES */
3657 putstr(WIN_STATUS
, 0, status_vals
[fldidx2
]);
3663 #ifdef STATUS_HILITES
3665 * status_threshold(int fldidx, int threshholdtype, anything threshold,
3666 * int behavior, int under, int over)
3668 * -- called when a hiliting preference is added, changed, or
3670 * -- the fldindex identifies which field is having its hiliting
3671 * preference set. It is an integer index value from botl.h
3672 * -- fldindex could be any one of the following from botl.h:
3673 * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3674 * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
3675 * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
3676 * BL_LEVELDESC, BL_EXP, BL_CONDITION
3677 * -- datatype is P_INT, P_UINT, P_LONG, or P_MASK.
3678 * -- threshold is an "anything" union which can contain the
3680 * -- behavior is used to define how threshold is used and can
3681 * be BL_TH_NONE, BL_TH_VAL_PERCENTAGE, BL_TH_VAL_ABSOLUTE,
3682 * or BL_TH_UPDOWN. BL_TH_NONE means don't do anything above
3683 * or below the threshold. BL_TH_VAL_PERCENTAGE treats the
3684 * threshold value as a precentage of the maximum possible
3685 * value. BL_TH_VAL_ABSOLUTE means that the threshold is an
3686 * actual value. BL_TH_UPDOWN means that threshold is not
3687 * used, and the two below/above hilite values indicate how
3688 * to display something going down (under) or rising (over).
3689 * -- under is the hilite attribute used if value is below the
3690 * threshold. The attribute can be BL_HILITE_NONE,
3691 * BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one
3692 * of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN,
3693 * CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY,
3694 * CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE,
3695 * CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15).
3696 * -- over is the hilite attribute used if value is at or above
3697 * the threshold. The attribute can be BL_HILITE_NONE,
3698 * BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one
3699 * of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN,
3700 * CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY,
3701 * CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE,
3702 * CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15).
3705 tty_status_threshold(fldidx
, thresholdtype
, threshold
, behavior
, under
, over
)
3706 int fldidx
, thresholdtype
;
3707 int behavior
, under
, over
;
3710 tty_status_hilites
[fldidx
].thresholdtype
= thresholdtype
;
3711 tty_status_hilites
[fldidx
].threshold
= threshold
;
3712 tty_status_hilites
[fldidx
].behavior
= behavior
;
3713 tty_status_hilites
[fldidx
].under
= under
;
3714 tty_status_hilites
[fldidx
].over
= over
;
3718 #endif /* STATUS_HILITES */
3719 #endif /*STATUS_VIA_WINDOWPORT*/
3721 #endif /* TTY_GRAPHICS */