1 /* NetHack 3.6 wintty.c $NHDT-Date: 1463614572 2016/05/18 23:36:12 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.131 $ */
2 /* Copyright (c) David Cohrs, 1991 */
3 /* NetHack may be freely redistributed. See license for details. */
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) || defined(_BULL_SOURCE)
39 #ifdef TTY_TILES_ESCCODES
40 extern short glyph2tile
[];
41 #define TILE_ANSI_COMMAND 'z'
42 #define AVTC_GLYPH_START 0
43 #define AVTC_GLYPH_END 1
44 #define AVTC_SELECT_WINDOW 2
45 #define AVTC_INLINE_SYNC 3
48 extern char mapped_menu_cmds
[]; /* from options.c */
50 /* this is only needed until tty_status_* routines are written */
51 extern NEARDATA winid WIN_STATUS
;
53 /* Interface definition, for windows.c */
54 struct window_procs tty_procs
= {
57 WC_TILED_MAP
| WC_ASCII_MAP
|
62 WC_COLOR
| WC_HILITE_PET
| WC_INVERSE
| WC_EIGHT_BIT_IN
,
63 #if defined(SELECTSAVED)
67 tty_init_nhwindows
, tty_player_selection
, tty_askname
, tty_get_nh_event
,
68 tty_exit_nhwindows
, tty_suspend_nhwindows
, tty_resume_nhwindows
,
69 tty_create_nhwindow
, tty_clear_nhwindow
, tty_display_nhwindow
,
70 tty_destroy_nhwindow
, tty_curs
, tty_putstr
, genl_putmixed
,
71 tty_display_file
, tty_start_menu
, tty_add_menu
, tty_end_menu
,
72 tty_select_menu
, tty_message_menu
, tty_update_inventory
, tty_mark_synch
,
78 tty_update_positionbar
,
80 tty_print_glyph
, tty_raw_print
, tty_raw_print_bold
, tty_nhgetch
,
81 tty_nh_poskey
, tty_nhbell
, tty_doprev_message
, tty_yn_function
,
82 tty_getlin
, tty_get_ext_cmd
, tty_number_pad
, tty_delay_output
,
83 #ifdef CHANGE_COLOR /* the Mac uses a palette device */
86 tty_change_background
, set_tty_font_name
,
91 /* other defs that really should go away (they're tty specific) */
92 tty_start_screen
, tty_end_screen
, genl_outrip
,
94 nttty_preference_update
,
96 genl_preference_update
,
98 tty_getmsghistory
, tty_putmsghistory
,
99 #ifdef STATUS_VIA_WINDOWPORT
101 genl_status_finish
, genl_status_enablefield
,
103 #ifdef STATUS_HILITES
104 tty_status_threshold
,
107 genl_can_suspend_yes
,
110 static int maxwin
= 0; /* number of windows in use */
112 struct WinDesc
*wins
[MAXWIN
];
113 struct DisplayDesc
*ttyDisplay
; /* the tty display descriptor */
115 extern void FDECL(cmov
, (int, int)); /* from termcap.c */
116 extern void FDECL(nocmov
, (int, int)); /* from termcap.c */
117 #if defined(UNIX) || defined(VMS)
118 static char obuf
[BUFSIZ
]; /* BUFSIZ is defined in stdio.h */
121 static char winpanicstr
[] = "Bad window id %d";
122 char defmorestr
[] = "--More--";
125 #if defined(USE_TILES) && defined(MSDOS)
126 boolean clipping
= FALSE
; /* clipping on? */
127 int clipx
= 0, clipxmax
= 0;
129 static boolean clipping
= FALSE
; /* clipping on? */
130 static int clipx
= 0, clipxmax
= 0;
132 static int clipy
= 0, clipymax
= 0;
133 #endif /* CLIPPING */
135 #if defined(USE_TILES) && defined(MSDOS)
136 extern void FDECL(adjust_cursor_flags
, (struct WinDesc
*));
139 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
140 boolean GFlag
= FALSE
;
141 boolean HE_resets_AS
; /* see termcap.c */
144 #if defined(MICRO) || defined(WIN32CON)
145 static const char to_continue
[] = "to continue";
146 #define getret() getreturn(to_continue)
148 STATIC_DCL
void NDECL(getret
);
150 STATIC_DCL
void FDECL(erase_menu_or_text
,
151 (winid
, struct WinDesc
*, BOOLEAN_P
));
152 STATIC_DCL
void FDECL(free_window_info
, (struct WinDesc
*, BOOLEAN_P
));
153 STATIC_DCL
void FDECL(dmore
, (struct WinDesc
*, const char *));
154 STATIC_DCL
void FDECL(set_item_state
, (winid
, int, tty_menu_item
*));
155 STATIC_DCL
void FDECL(set_all_on_page
, (winid
, tty_menu_item
*,
157 STATIC_DCL
void FDECL(unset_all_on_page
, (winid
, tty_menu_item
*,
159 STATIC_DCL
void FDECL(invert_all_on_page
, (winid
, tty_menu_item
*,
160 tty_menu_item
*, CHAR_P
));
161 STATIC_DCL
void FDECL(invert_all
, (winid
, tty_menu_item
*,
162 tty_menu_item
*, CHAR_P
));
163 STATIC_DCL
void FDECL(toggle_menu_attr
, (BOOLEAN_P
, int, int));
164 STATIC_DCL
void FDECL(process_menu_window
, (winid
, struct WinDesc
*));
165 STATIC_DCL
void FDECL(process_text_window
, (winid
, struct WinDesc
*));
166 STATIC_DCL tty_menu_item
*FDECL(reverse
, (tty_menu_item
*));
167 STATIC_DCL
const char *FDECL(compress_str
, (const char *));
168 STATIC_DCL
void FDECL(tty_putsym
, (winid
, int, int, CHAR_P
));
169 STATIC_DCL
void FDECL(bail
, (const char *)); /* __attribute__((noreturn)) */
170 STATIC_DCL
void FDECL(setup_rolemenu
, (winid
, BOOLEAN_P
, int, int, int));
171 STATIC_DCL
void FDECL(setup_racemenu
, (winid
, BOOLEAN_P
, int, int, int));
172 STATIC_DCL
void FDECL(setup_gendmenu
, (winid
, BOOLEAN_P
, int, int, int));
173 STATIC_DCL
void FDECL(setup_algnmenu
, (winid
, BOOLEAN_P
, int, int, int));
174 STATIC_DCL boolean
NDECL(reset_role_filtering
);
177 * A string containing all the default commands -- to add to a list
178 * of acceptable inputs.
180 static const char default_menu_cmds
[] = {
181 MENU_FIRST_PAGE
, MENU_LAST_PAGE
, MENU_NEXT_PAGE
,
182 MENU_PREVIOUS_PAGE
, MENU_SELECT_ALL
, MENU_UNSELECT_ALL
,
183 MENU_INVERT_ALL
, MENU_SELECT_PAGE
, MENU_UNSELECT_PAGE
,
184 MENU_INVERT_PAGE
, MENU_SEARCH
, 0 /* null terminator */
187 #ifdef TTY_TILES_ESCCODES
188 static int vt_tile_current_window
= -2;
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)
229 STATIC_DCL
void FDECL(winch_handler
, (int));
232 * This really ought to just set a flag like the hangup handler does,
233 * then check the flag at "safe" times, in case the signal arrives
234 * while something fragile is executing. Better to have a brief period
235 * where display updates don't fit the new size than for tty internals
236 * to become corrupted.
238 * 'winch_seen' has been "notyet" for a long time....
242 winch_handler(sig_unused
) /* signal handler is called with at least 1 arg */
243 int sig_unused UNUSED
;
245 int oldLI
= LI
, oldCO
= CO
, i
;
246 register struct WinDesc
*cw
;
250 #define WINCH_MESSAGE "(SIGWINCH)"
252 (void) write(fileno(wc_tracelogf
), WINCH_MESSAGE
,
253 strlen(WINCH_MESSAGE
));
258 /* For long running events such as multi-page menus and
259 * display_file(), we note the signal's occurance and
260 * hope the code there decides to handle the situation
261 * and reset the flag. There will be race conditions
262 * when handling this - code handlers so it doesn't matter.
267 if ((oldLI
!= LI
|| oldCO
!= CO
) && ttyDisplay
) {
268 ttyDisplay
->rows
= LI
;
269 ttyDisplay
->cols
= CO
;
271 cw
= wins
[BASE_WINDOW
];
272 cw
->rows
= ttyDisplay
->rows
;
273 cw
->cols
= ttyDisplay
->cols
;
275 if (iflags
.window_inited
) {
276 cw
= wins
[WIN_MESSAGE
];
277 cw
->curx
= cw
->cury
= 0;
279 tty_destroy_nhwindow(WIN_STATUS
);
280 WIN_STATUS
= tty_create_nhwindow(NHW_STATUS
);
284 if (CO
< COLNO
|| LI
< ROWNO
+ 3) {
286 tty_cliparound(u
.ux
, u
.uy
);
292 i
= ttyDisplay
->toplin
;
293 ttyDisplay
->toplin
= 0;
296 ttyDisplay
->toplin
= i
;
301 for (i
= WIN_INVEN
; i
< MAXWIN
; i
++)
302 if (wins
[i
] && wins
[i
]->active
) {
304 addtopl("Press Return to continue: ");
307 (void) fflush(stdout
);
318 tty_init_nhwindows(argcp
, argv
)
325 * Remember tty modes, to be restored on exit.
327 * gettty() must be called before tty_startup()
328 * due to ordering of LI/CO settings
329 * tty_startup() must be called before initoptions()
330 * due to ordering of graphics settings
332 #if defined(UNIX) || defined(VMS)
333 setbuf(stdout
, obuf
);
337 /* to port dependant tty setup */
338 tty_startup(&wid
, &hgt
);
339 setftty(); /* calls start_screen */
341 /* set up tty descriptor */
342 ttyDisplay
= (struct DisplayDesc
*) alloc(sizeof(struct DisplayDesc
));
343 ttyDisplay
->toplin
= 0;
344 ttyDisplay
->rows
= hgt
;
345 ttyDisplay
->cols
= wid
;
346 ttyDisplay
->curx
= ttyDisplay
->cury
= 0;
347 ttyDisplay
->inmore
= ttyDisplay
->inread
= ttyDisplay
->intr
= 0;
348 ttyDisplay
->dismiss_more
= 0;
350 ttyDisplay
->color
= NO_COLOR
;
352 ttyDisplay
->attrs
= 0;
354 /* set up the default windows */
355 BASE_WINDOW
= tty_create_nhwindow(NHW_BASE
);
356 wins
[BASE_WINDOW
]->active
= 1;
358 ttyDisplay
->lastwin
= WIN_ERR
;
360 #if defined(SIGWINCH) && defined(CLIPPING) && !defined(NO_SIGNAL)
361 (void) signal(SIGWINCH
, (SIG_RET_TYPE
) winch_handler
);
364 tty_clear_nhwindow(BASE_WINDOW
);
366 tty_putstr(BASE_WINDOW
, 0, "");
367 for (i
= 1; i
<= 4; ++i
)
368 tty_putstr(BASE_WINDOW
, 0, copyright_banner_line(i
));
369 tty_putstr(BASE_WINDOW
, 0, "");
370 tty_display_nhwindow(BASE_WINDOW
, FALSE
);
373 /* try to reduce clutter in the code below... */
374 #define ROLE flags.initrole
375 #define RACE flags.initrace
376 #define GEND flags.initgend
377 #define ALGN flags.initalign
380 tty_player_selection()
382 int i
, k
, n
, choice
, nextpick
;
383 boolean getconfirmation
, picksomething
;
385 char pbuf
[QBUFSZ
], plbuf
[QBUFSZ
];
388 menu_item
*selected
= 0;
390 /* Used to avoid "Is this ok?" if player has already specified all
391 * four facets of role.
392 * Note that rigid_role_checks might force any unspecified facets to
393 * have a specific value, but that will still require confirmation;
394 * player can specify the forced ones if avoiding that is demanded.
396 picksomething
= (ROLE
== ROLE_NONE
|| RACE
== ROLE_NONE
397 || GEND
== ROLE_NONE
|| ALGN
== ROLE_NONE
);
399 * choose randomly without asking for all unspecified facets.
401 if (flags
.randomall
&& picksomething
) {
402 if (ROLE
== ROLE_NONE
)
404 if (RACE
== ROLE_NONE
)
406 if (GEND
== ROLE_NONE
)
408 if (ALGN
== ROLE_NONE
)
412 /* prevent unnecessary prompting if role forces race (samurai) or gender
413 (valkyrie) or alignment (rogue), or race forces alignment (orc), &c */
416 /* Should we randomly pick for the player? */
417 if (ROLE
== ROLE_NONE
|| RACE
== ROLE_NONE
|| GEND
== ROLE_NONE
418 || ALGN
== ROLE_NONE
) {
420 char *prompt
= build_plselection_prompt(pbuf
, QBUFSZ
,
421 ROLE
, RACE
, GEND
, ALGN
);
423 /* this prompt string ends in "[ynaq]?":
424 y - game picks role,&c then asks player to confirm;
425 n - player manually chooses via menu selections;
426 a - like 'y', but skips confirmation and starts game;
429 tty_putstr(BASE_WINDOW
, 0, "");
430 echoline
= wins
[BASE_WINDOW
]->cury
;
431 tty_putstr(BASE_WINDOW
, 0, prompt
);
433 pick4u
= lowc(readchar());
434 if (index(quitchars
, pick4u
))
436 } while (!index(ynaqchars
, pick4u
));
437 if ((int) strlen(prompt
) + 1 < CO
) {
438 /* Echo choice and move back down line */
439 tty_putsym(BASE_WINDOW
, (int) strlen(prompt
) + 1, echoline
,
441 tty_putstr(BASE_WINDOW
, 0, "");
443 /* Otherwise it's hard to tell where to echo, and things are
444 * wrapping a bit messily anyway, so (try to) make sure the next
445 * question shows up well and doesn't get wrapped at the
446 * bottom of the window.
448 tty_clear_nhwindow(BASE_WINDOW
);
450 if (pick4u
!= 'y' && pick4u
!= 'a' && pick4u
!= 'n')
457 if (nextpick
== RS_ROLE
) {
459 /* Select a role, if necessary;
460 we'll try to be compatible with pre-selected
461 race/gender/alignment, but may not succeed. */
463 /* Process the choice */
464 if (pick4u
== 'y' || pick4u
== 'a' || ROLE
== ROLE_RANDOM
) {
465 /* Pick a random role */
466 k
= pick_role(RACE
, GEND
, ALGN
, PICK_RANDOM
);
468 tty_putstr(BASE_WINDOW
, 0, "Incompatible role!");
472 /* Prompt for a role */
473 tty_clear_nhwindow(BASE_WINDOW
);
474 role_selection_prolog(RS_ROLE
, BASE_WINDOW
);
475 win
= create_nhwindow(NHW_MENU
);
477 /* populate the menu with role choices */
478 setup_rolemenu(win
, TRUE
, RACE
, GEND
, ALGN
);
479 /* add miscellaneous menu entries */
480 role_menu_extra(ROLE_RANDOM
, win
, TRUE
);
481 any
.a_int
= 0; /* separator, not a choice */
482 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "",
484 role_menu_extra(RS_RACE
, win
, FALSE
);
485 role_menu_extra(RS_GENDER
, win
, FALSE
);
486 role_menu_extra(RS_ALGNMNT
, win
, FALSE
);
488 role_menu_extra(RS_filter
, win
, FALSE
);
489 role_menu_extra(ROLE_NONE
, win
, FALSE
); /* quit */
490 Strcpy(pbuf
, "Pick a role or profession");
492 n
= select_menu(win
, PICK_ONE
, &selected
);
494 * PICK_ONE with preselected choice behaves strangely:
495 * n == -1 -- <escape>, so use quit choice;
496 * n == 0 -- explicitly chose preselected entry,
497 * toggling it off, so use it;
498 * n == 1 -- implicitly chose preselected entry
499 * with <space> or <return>;
500 * n == 2 -- explicitly chose a different entry, so
501 * both it and preselected one are in list.
504 choice
= selected
[0].item
.a_int
;
505 if (n
> 1 && choice
== ROLE_RANDOM
)
506 choice
= selected
[1].item
.a_int
;
508 choice
= (n
== 0) ? ROLE_RANDOM
: ROLE_NONE
;
510 free((genericptr_t
) selected
), selected
= 0;
511 destroy_nhwindow(win
);
513 if (choice
== ROLE_NONE
) {
514 goto give_up
; /* Selected quit */
515 } else if (choice
== RS_menu_arg(RS_ALGNMNT
)) {
516 ALGN
= k
= ROLE_NONE
;
517 nextpick
= RS_ALGNMNT
;
518 } else if (choice
== RS_menu_arg(RS_GENDER
)) {
519 GEND
= k
= ROLE_NONE
;
520 nextpick
= RS_GENDER
;
521 } else if (choice
== RS_menu_arg(RS_RACE
)) {
522 RACE
= k
= ROLE_NONE
;
524 } else if (choice
== RS_menu_arg(RS_filter
)) {
525 ROLE
= k
= ROLE_NONE
;
526 (void) reset_role_filtering();
528 } else if (choice
== ROLE_RANDOM
) {
529 k
= pick_role(RACE
, GEND
, ALGN
, PICK_RANDOM
);
540 if (nextpick
== RS_RACE
) {
541 nextpick
= (ROLE
< 0) ? RS_ROLE
: RS_GENDER
;
542 /* Select a race, if necessary;
543 force compatibility with role, try for compatibility
544 with pre-selected gender/alignment. */
545 if (RACE
< 0 || !validrace(ROLE
, RACE
)) {
546 /* no race yet, or pre-selected race not valid */
547 if (pick4u
== 'y' || pick4u
== 'a' || RACE
== ROLE_RANDOM
) {
548 k
= pick_race(ROLE
, GEND
, ALGN
, PICK_RANDOM
);
550 tty_putstr(BASE_WINDOW
, 0, "Incompatible race!");
553 } else { /* pick4u == 'n' */
554 /* Count the number of valid races */
555 n
= 0; /* number valid */
556 k
= 0; /* valid race */
557 for (i
= 0; races
[i
].noun
; i
++)
558 if (ok_race(ROLE
, i
, GEND
, ALGN
)) {
563 for (i
= 0; races
[i
].noun
; i
++)
564 if (validrace(ROLE
, i
)) {
569 /* Permit the user to pick, if there is more than one */
571 tty_clear_nhwindow(BASE_WINDOW
);
572 role_selection_prolog(RS_RACE
, BASE_WINDOW
);
573 win
= create_nhwindow(NHW_MENU
);
575 any
= zeroany
; /* zero out all bits */
576 /* populate the menu with role choices */
577 setup_racemenu(win
, TRUE
, ROLE
, GEND
, ALGN
);
578 /* add miscellaneous menu entries */
579 role_menu_extra(ROLE_RANDOM
, win
, TRUE
);
580 any
.a_int
= 0; /* separator, not a choice */
581 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "",
583 role_menu_extra(RS_ROLE
, win
, FALSE
);
584 role_menu_extra(RS_GENDER
, win
, FALSE
);
585 role_menu_extra(RS_ALGNMNT
, win
, FALSE
);
587 role_menu_extra(RS_filter
, win
, FALSE
);
588 role_menu_extra(ROLE_NONE
, win
, FALSE
); /* quit */
589 Strcpy(pbuf
, "Pick a race or species");
591 n
= select_menu(win
, PICK_ONE
, &selected
);
593 choice
= selected
[0].item
.a_int
;
594 if (n
> 1 && choice
== ROLE_RANDOM
)
595 choice
= selected
[1].item
.a_int
;
597 choice
= (n
== 0) ? ROLE_RANDOM
: ROLE_NONE
;
599 free((genericptr_t
) selected
), selected
= 0;
600 destroy_nhwindow(win
);
602 if (choice
== ROLE_NONE
) {
603 goto give_up
; /* Selected quit */
604 } else if (choice
== RS_menu_arg(RS_ALGNMNT
)) {
605 ALGN
= k
= ROLE_NONE
;
606 nextpick
= RS_ALGNMNT
;
607 } else if (choice
== RS_menu_arg(RS_GENDER
)) {
608 GEND
= k
= ROLE_NONE
;
609 nextpick
= RS_GENDER
;
610 } else if (choice
== RS_menu_arg(RS_ROLE
)) {
611 ROLE
= k
= ROLE_NONE
;
613 } else if (choice
== RS_menu_arg(RS_filter
)) {
614 RACE
= k
= ROLE_NONE
;
615 if (reset_role_filtering())
619 } else if (choice
== ROLE_RANDOM
) {
620 k
= pick_race(ROLE
, GEND
, ALGN
, PICK_RANDOM
);
632 if (nextpick
== RS_GENDER
) {
633 nextpick
= (ROLE
< 0) ? RS_ROLE
: (RACE
< 0) ? RS_RACE
635 /* Select a gender, if necessary;
636 force compatibility with role/race, try for compatibility
637 with pre-selected alignment. */
638 if (GEND
< 0 || !validgend(ROLE
, RACE
, GEND
)) {
639 /* no gender yet, or pre-selected gender not valid */
640 if (pick4u
== 'y' || pick4u
== 'a' || GEND
== ROLE_RANDOM
) {
641 k
= pick_gend(ROLE
, RACE
, ALGN
, PICK_RANDOM
);
643 tty_putstr(BASE_WINDOW
, 0, "Incompatible gender!");
644 k
= randgend(ROLE
, RACE
);
646 } else { /* pick4u == 'n' */
647 /* Count the number of valid genders */
648 n
= 0; /* number valid */
649 k
= 0; /* valid gender */
650 for (i
= 0; i
< ROLE_GENDERS
; i
++)
651 if (ok_gend(ROLE
, RACE
, i
, ALGN
)) {
656 for (i
= 0; i
< ROLE_GENDERS
; i
++)
657 if (validgend(ROLE
, RACE
, i
)) {
662 /* Permit the user to pick, if there is more than one */
664 tty_clear_nhwindow(BASE_WINDOW
);
665 role_selection_prolog(RS_GENDER
, BASE_WINDOW
);
666 win
= create_nhwindow(NHW_MENU
);
668 any
= zeroany
; /* zero out all bits */
669 /* populate the menu with gender choices */
670 setup_gendmenu(win
, TRUE
, ROLE
, RACE
, ALGN
);
671 /* add miscellaneous menu entries */
672 role_menu_extra(ROLE_RANDOM
, win
, TRUE
);
673 any
.a_int
= 0; /* separator, not a choice */
674 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "",
676 role_menu_extra(RS_ROLE
, win
, FALSE
);
677 role_menu_extra(RS_RACE
, win
, FALSE
);
678 role_menu_extra(RS_ALGNMNT
, win
, FALSE
);
680 role_menu_extra(RS_filter
, win
, FALSE
);
681 role_menu_extra(ROLE_NONE
, win
, FALSE
); /* quit */
682 Strcpy(pbuf
, "Pick a gender or sex");
684 n
= select_menu(win
, PICK_ONE
, &selected
);
686 choice
= selected
[0].item
.a_int
;
687 if (n
> 1 && choice
== ROLE_RANDOM
)
688 choice
= selected
[1].item
.a_int
;
690 choice
= (n
== 0) ? ROLE_RANDOM
: ROLE_NONE
;
692 free((genericptr_t
) selected
), selected
= 0;
693 destroy_nhwindow(win
);
695 if (choice
== ROLE_NONE
) {
696 goto give_up
; /* Selected quit */
697 } else if (choice
== RS_menu_arg(RS_ALGNMNT
)) {
698 ALGN
= k
= ROLE_NONE
;
699 nextpick
= RS_ALGNMNT
;
700 } else if (choice
== RS_menu_arg(RS_RACE
)) {
701 RACE
= k
= ROLE_NONE
;
703 } else if (choice
== RS_menu_arg(RS_ROLE
)) {
704 ROLE
= k
= ROLE_NONE
;
706 } else if (choice
== RS_menu_arg(RS_filter
)) {
707 GEND
= k
= ROLE_NONE
;
708 if (reset_role_filtering())
711 nextpick
= RS_GENDER
;
712 } else if (choice
== ROLE_RANDOM
) {
713 k
= pick_gend(ROLE
, RACE
, ALGN
, PICK_RANDOM
);
715 k
= randgend(ROLE
, RACE
);
722 } /* needed gender */
723 } /* picking gender */
725 if (nextpick
== RS_ALGNMNT
) {
726 nextpick
= (ROLE
< 0) ? RS_ROLE
: (RACE
< 0) ? RS_RACE
: RS_GENDER
;
727 /* Select an alignment, if necessary;
728 force compatibility with role/race/gender. */
729 if (ALGN
< 0 || !validalign(ROLE
, RACE
, ALGN
)) {
730 /* no alignment yet, or pre-selected alignment not valid */
731 if (pick4u
== 'y' || pick4u
== 'a' || ALGN
== ROLE_RANDOM
) {
732 k
= pick_align(ROLE
, RACE
, GEND
, PICK_RANDOM
);
734 tty_putstr(BASE_WINDOW
, 0, "Incompatible alignment!");
735 k
= randalign(ROLE
, RACE
);
737 } else { /* pick4u == 'n' */
738 /* Count the number of valid alignments */
739 n
= 0; /* number valid */
740 k
= 0; /* valid alignment */
741 for (i
= 0; i
< ROLE_ALIGNS
; i
++)
742 if (ok_align(ROLE
, RACE
, GEND
, i
)) {
747 for (i
= 0; i
< ROLE_ALIGNS
; i
++)
748 if (validalign(ROLE
, RACE
, i
)) {
753 /* Permit the user to pick, if there is more than one */
755 tty_clear_nhwindow(BASE_WINDOW
);
756 role_selection_prolog(RS_ALGNMNT
, BASE_WINDOW
);
757 win
= create_nhwindow(NHW_MENU
);
759 any
= zeroany
; /* zero out all bits */
760 setup_algnmenu(win
, TRUE
, ROLE
, RACE
, GEND
);
761 role_menu_extra(ROLE_RANDOM
, win
, TRUE
);
762 any
.a_int
= 0; /* separator, not a choice */
763 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "",
765 role_menu_extra(RS_ROLE
, win
, FALSE
);
766 role_menu_extra(RS_RACE
, win
, FALSE
);
767 role_menu_extra(RS_GENDER
, win
, FALSE
);
769 role_menu_extra(RS_filter
, win
, FALSE
);
770 role_menu_extra(ROLE_NONE
, win
, FALSE
); /* quit */
771 Strcpy(pbuf
, "Pick an alignment or creed");
773 n
= select_menu(win
, PICK_ONE
, &selected
);
775 choice
= selected
[0].item
.a_int
;
776 if (n
> 1 && choice
== ROLE_RANDOM
)
777 choice
= selected
[1].item
.a_int
;
779 choice
= (n
== 0) ? ROLE_RANDOM
: ROLE_NONE
;
781 free((genericptr_t
) selected
), selected
= 0;
782 destroy_nhwindow(win
);
784 if (choice
== ROLE_NONE
) {
785 goto give_up
; /* Selected quit */
786 } else if (choice
== RS_menu_arg(RS_GENDER
)) {
787 GEND
= k
= ROLE_NONE
;
788 nextpick
= RS_GENDER
;
789 } else if (choice
== RS_menu_arg(RS_RACE
)) {
790 RACE
= k
= ROLE_NONE
;
792 } else if (choice
== RS_menu_arg(RS_ROLE
)) {
793 ROLE
= k
= ROLE_NONE
;
795 } else if (choice
== RS_menu_arg(RS_filter
)) {
796 ALGN
= k
= ROLE_NONE
;
797 if (reset_role_filtering())
800 nextpick
= RS_ALGNMNT
;
801 } else if (choice
== ROLE_RANDOM
) {
802 k
= pick_align(ROLE
, RACE
, GEND
, PICK_RANDOM
);
804 k
= randalign(ROLE
, RACE
);
811 } /* needed alignment */
812 } /* picking alignment */
814 } while (ROLE
< 0 || RACE
< 0 || GEND
< 0 || ALGN
< 0);
817 * Role, race, &c have now been determined;
818 * ask for confirmation and maybe go back to choose all over again.
820 * Uses ynaq for familiarity, although 'a' is usually a
821 * superset of 'y' but here is an alternate form of 'n'.
823 * title: Is this ok? [ynaq]
825 * text: $name, $alignment $gender $race $role
827 * menu: y + yes; play
829 * maybe: a - no; rename hero
833 getconfirmation
= (picksomething
&& pick4u
!= 'a' && !flags
.randomall
);
834 while (getconfirmation
) {
835 tty_clear_nhwindow(BASE_WINDOW
);
836 role_selection_prolog(ROLE_NONE
, BASE_WINDOW
);
837 win
= create_nhwindow(NHW_MENU
);
839 any
= zeroany
; /* zero out all bits */
841 if (!roles
[ROLE
].name
.f
842 && (roles
[ROLE
].allow
& ROLE_GENDMASK
)
843 == (ROLE_MALE
| ROLE_FEMALE
))
844 Sprintf(plbuf
, " %s", genders
[GEND
].adj
);
846 *plbuf
= '\0'; /* omit redundant gender */
847 Sprintf(pbuf
, "%s, %s%s %s %s", plname
, aligns
[ALGN
].adj
, plbuf
,
849 (GEND
== 1 && roles
[ROLE
].name
.f
) ? roles
[ROLE
].name
.f
850 : roles
[ROLE
].name
.m
);
851 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, pbuf
,
853 /* blank separator */
855 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "", MENU_UNSELECTED
);
856 /* [ynaq] menu choices */
858 add_menu(win
, NO_GLYPH
, &any
, 'y', 0, ATR_NONE
, "Yes; start game",
861 add_menu(win
, NO_GLYPH
, &any
, 'n', 0, ATR_NONE
,
862 "No; choose role again", MENU_UNSELECTED
);
863 if (iflags
.renameallowed
) {
865 add_menu(win
, NO_GLYPH
, &any
, 'a', 0, ATR_NONE
,
866 "Not yet; choose another name", MENU_UNSELECTED
);
869 add_menu(win
, NO_GLYPH
, &any
, 'q', 0, ATR_NONE
, "Quit",
871 Sprintf(pbuf
, "Is this ok? [yn%sq]", iflags
.renameallowed
? "a" : "");
873 n
= select_menu(win
, PICK_ONE
, &selected
);
874 /* [pick-one menus with a preselected entry behave oddly...] */
875 choice
= (n
> 0) ? selected
[n
- 1].item
.a_int
: (n
== 0) ? 1 : -1;
877 free((genericptr_t
) selected
), selected
= 0;
878 destroy_nhwindow(win
);
881 default: /* 'q' or ESC */
882 goto give_up
; /* quit */
886 * TODO: what, if anything, should be done if the name is
887 * changed to or from "wizard" after port-specific startup
888 * code has set flags.debug based on the original name?
890 int saveROLE
, saveRACE
, saveGEND
, saveALGN
;
892 iflags
.renameinprogress
= TRUE
;
893 /* plnamesuffix() can change any or all of ROLE, RACE,
894 GEND, ALGN; we'll override that and honor only the name */
895 saveROLE
= ROLE
, saveRACE
= RACE
, saveGEND
= GEND
,
898 plnamesuffix(); /* calls askname() when plname[] is empty */
899 ROLE
= saveROLE
, RACE
= saveRACE
, GEND
= saveGEND
,
901 break; /* getconfirmation is still True */
904 /* start fresh, but bypass "shall I pick everything for you?"
905 step; any partial role selection via config file, command
906 line, or name suffix is discarded this time */
908 ROLE
= RACE
= GEND
= ALGN
= ROLE_NONE
;
911 case 1: /* 'y' or Space or Return/Enter */
912 /* success; drop out through end of function */
913 getconfirmation
= FALSE
;
919 tty_display_nhwindow(BASE_WINDOW
, FALSE
);
925 free((genericptr_t
) selected
); /* [obsolete] */
932 reset_role_filtering()
937 menu_item
*selected
= 0;
939 win
= create_nhwindow(NHW_MENU
);
943 /* no extra blank line preceding this entry; end_menu supplies one */
944 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
945 "Unacceptable roles", MENU_UNSELECTED
);
946 setup_rolemenu(win
, FALSE
, ROLE_NONE
, ROLE_NONE
, ROLE_NONE
);
948 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "", MENU_UNSELECTED
);
949 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
950 "Unacceptable races", MENU_UNSELECTED
);
951 setup_racemenu(win
, FALSE
, ROLE_NONE
, ROLE_NONE
, ROLE_NONE
);
953 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "", MENU_UNSELECTED
);
954 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
955 "Unacceptable genders", MENU_UNSELECTED
);
956 setup_gendmenu(win
, FALSE
, ROLE_NONE
, ROLE_NONE
, ROLE_NONE
);
958 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "", MENU_UNSELECTED
);
959 add_menu(win
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
960 "Uncceptable alignments", MENU_UNSELECTED
);
961 setup_algnmenu(win
, FALSE
, ROLE_NONE
, ROLE_NONE
, ROLE_NONE
);
963 end_menu(win
, "Pick all that apply");
964 n
= select_menu(win
, PICK_ANY
, &selected
);
968 for (i
= 0; i
< n
; i
++)
969 setrolefilter(selected
[i
].item
.a_string
);
971 ROLE
= RACE
= GEND
= ALGN
= ROLE_NONE
;
974 free((genericptr_t
) selected
), selected
= 0;
975 destroy_nhwindow(win
);
976 return (n
> 0) ? TRUE
: FALSE
;
984 /* add entries a-Archeologist, b-Barbarian, &c to menu being built in 'win' */
986 setup_rolemenu(win
, filtering
, race
, gend
, algn
)
988 boolean filtering
; /* True => exclude filtered roles; False => filter reset */
989 int race
, gend
, algn
; /* all ROLE_NONE for !filtering case */
994 char thisch
, lastch
= '\0', rolenamebuf
[50];
996 any
= zeroany
; /* zero out all bits */
997 for (i
= 0; roles
[i
].name
.m
; i
++) {
998 role_ok
= ok_role(i
, race
, gend
, algn
);
999 if (filtering
&& !role_ok
)
1004 any
.a_string
= roles
[i
].name
.m
;
1005 thisch
= lowc(*roles
[i
].name
.m
);
1006 if (thisch
== lastch
)
1007 thisch
= highc(thisch
);
1008 Strcpy(rolenamebuf
, roles
[i
].name
.m
);
1009 if (roles
[i
].name
.f
) {
1010 /* role has distinct name for female (C,P) */
1012 /* female already chosen; replace male name */
1013 Strcpy(rolenamebuf
, roles
[i
].name
.f
);
1014 } else if (gend
< 0) {
1015 /* not chosen yet; append slash+female name */
1016 Strcat(rolenamebuf
, "/");
1017 Strcat(rolenamebuf
, roles
[i
].name
.f
);
1020 /* !filtering implies reset_role_filtering() where we want to
1021 mark this role as preseleted if current filter excludes it */
1022 add_menu(win
, NO_GLYPH
, &any
, thisch
, 0, ATR_NONE
, an(rolenamebuf
),
1023 (!filtering
&& !role_ok
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1029 setup_racemenu(win
, filtering
, role
, gend
, algn
)
1032 int role
, gend
, algn
;
1040 for (i
= 0; races
[i
].noun
; i
++) {
1041 race_ok
= ok_race(role
, i
, gend
, algn
);
1042 if (filtering
&& !race_ok
)
1047 any
.a_string
= races
[i
].noun
;
1048 this_ch
= *races
[i
].noun
;
1049 /* filtering: picking race, so choose by first letter, with
1050 capital letter as unseen accelerator;
1051 !filtering: resetting filter rather than picking, choose by
1052 capital letter since lowercase role letters will be present */
1053 add_menu(win
, NO_GLYPH
, &any
,
1054 filtering
? this_ch
: highc(this_ch
),
1055 filtering
? highc(this_ch
) : 0,
1056 ATR_NONE
, races
[i
].noun
,
1057 (!filtering
&& !race_ok
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1062 setup_gendmenu(win
, filtering
, role
, race
, algn
)
1065 int role
, race
, algn
;
1073 for (i
= 0; i
< ROLE_GENDERS
; i
++) {
1074 gend_ok
= ok_gend(role
, race
, i
, algn
);
1075 if (filtering
&& !gend_ok
)
1080 any
.a_string
= genders
[i
].adj
;
1081 this_ch
= *genders
[i
].adj
;
1082 /* (see setup_racemenu for explanation of selector letters
1083 and setup_rolemenu for preselection) */
1084 add_menu(win
, NO_GLYPH
, &any
,
1085 filtering
? this_ch
: highc(this_ch
),
1086 filtering
? highc(this_ch
) : 0,
1087 ATR_NONE
, genders
[i
].adj
,
1088 (!filtering
&& !gend_ok
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1093 setup_algnmenu(win
, filtering
, role
, race
, gend
)
1096 int role
, race
, gend
;
1104 for (i
= 0; i
< ROLE_ALIGNS
; i
++) {
1105 algn_ok
= ok_align(role
, race
, gend
, i
);
1106 if (filtering
&& !algn_ok
)
1111 any
.a_string
= aligns
[i
].adj
;
1112 this_ch
= *aligns
[i
].adj
;
1113 /* (see setup_racemenu for explanation of selector letters
1114 and setup_rolemenu for preselection) */
1115 add_menu(win
, NO_GLYPH
, &any
,
1116 filtering
? this_ch
: highc(this_ch
),
1117 filtering
? highc(this_ch
) : 0,
1118 ATR_NONE
, aligns
[i
].adj
,
1119 (!filtering
&& !algn_ok
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1124 * plname is filled either by an option (-u Player or -uPlayer) or
1125 * explicitly (by being the wizard) or by askname.
1126 * It may still contain a suffix denoting the role, etc.
1127 * Always called after init_nhwindows() and before display_gamewindows().
1132 static const char who_are_you
[] = "Who are you? ";
1133 register int c
, ct
, tryct
= 0;
1136 if (iflags
.wc2_selectsaved
&& !iflags
.renameinprogress
)
1137 switch (restore_menu(BASE_WINDOW
)) {
1139 bail("Until next time then..."); /* quit */
1142 break; /* no game chosen; start new game */
1144 return; /* plname[] has been set */
1146 #endif /* SELECTSAVED */
1148 tty_putstr(BASE_WINDOW
, 0, "");
1152 bail("Giving up after 10 tries.\n");
1153 tty_curs(BASE_WINDOW
, 1, wins
[BASE_WINDOW
]->cury
- 1);
1154 tty_putstr(BASE_WINDOW
, 0, "Enter a name for your character...");
1155 /* erase previous prompt (in case of ESC after partial response)
1157 tty_curs(BASE_WINDOW
, 1, wins
[BASE_WINDOW
]->cury
), cl_end();
1159 tty_putstr(BASE_WINDOW
, 0, who_are_you
);
1160 tty_curs(BASE_WINDOW
, (int) (sizeof who_are_you
),
1161 wins
[BASE_WINDOW
]->cury
- 1);
1163 while ((c
= tty_nhgetch()) != '\n') {
1169 } /* continue outer loop */
1170 #if defined(WIN32CON)
1172 bail("^C abort.\n");
1174 /* some people get confused when their erase char is not ^H */
1175 if (c
== '\b' || c
== '\177') {
1181 #if defined(MICRO) || defined(WIN32CON)
1182 #if defined(WIN32CON) || defined(MSDOS)
1183 backsp(); /* \b is visible on NT */
1184 (void) putchar(' ');
1190 (void) putchar('\b');
1191 (void) putchar(' ');
1192 (void) putchar('\b');
1197 #if defined(UNIX) || defined(VMS)
1198 if (c
!= '-' && c
!= '@')
1199 if (!(c
>= 'a' && c
<= 'z') && !(c
>= 'A' && c
<= 'Z') &&
1200 /* reject leading digit but allow digits elsewhere
1201 (avoids ambiguity when character name gets
1202 appended to uid to construct save file name) */
1203 !(c
>= '0' && c
<= '9' && ct
> 0))
1206 if (ct
< (int) (sizeof plname
) - 1) {
1209 if (iflags
.grmode
) {
1226 /* move to next line to simulate echo of user's <return> */
1227 tty_curs(BASE_WINDOW
, 1, wins
[BASE_WINDOW
]->cury
+ 1);
1229 /* since we let user pick an arbitrary name now, he/she can pick
1230 another one during role selection */
1231 iflags
.renameallowed
= TRUE
;
1240 #if !defined(MICRO) && !defined(WIN32CON)
1248 xputs(iflags
.cbreak
? "space" : "return");
1249 xputs(" to continue: ");
1257 tty_suspend_nhwindows(str
)
1260 settty(str
); /* calls end_screen, perhaps raw_print */
1262 tty_raw_print(""); /* calls fflush(stdout) */
1266 tty_resume_nhwindows()
1269 setftty(); /* calls start_screen */
1274 tty_exit_nhwindows(str
)
1279 tty_suspend_nhwindows(str
);
1281 * Disable windows to avoid calls to window routines.
1283 free_pickinv_cache(); /* reset its state as well as tear down window */
1284 for (i
= 0; i
< MAXWIN
; i
++) {
1285 if (i
== BASE_WINDOW
)
1286 continue; /* handle wins[BASE_WINDOW] last */
1288 #ifdef FREE_ALL_MEMORY
1289 free_window_info(wins
[i
], TRUE
);
1290 free((genericptr_t
) wins
[i
]);
1292 wins
[i
] = (struct WinDesc
*) 0;
1295 WIN_MAP
= WIN_MESSAGE
= WIN_INVEN
= WIN_ERR
; /* these are all gone now */
1296 #ifndef STATUS_VIA_WINDOWPORT
1297 WIN_STATUS
= WIN_ERR
;
1299 #ifdef FREE_ALL_MEMORY
1300 if (BASE_WINDOW
!= WIN_ERR
&& wins
[BASE_WINDOW
]) {
1301 free_window_info(wins
[BASE_WINDOW
], TRUE
);
1302 free((genericptr_t
) wins
[BASE_WINDOW
]);
1303 wins
[BASE_WINDOW
] = (struct WinDesc
*) 0;
1304 BASE_WINDOW
= WIN_ERR
;
1306 free((genericptr_t
) ttyDisplay
);
1307 ttyDisplay
= (struct DisplayDesc
*) 0;
1310 #ifndef NO_TERMS /*(until this gets added to the window interface)*/
1311 tty_shutdown(); /* cleanup termcap/terminfo/whatever */
1313 iflags
.window_inited
= 0;
1317 tty_create_nhwindow(type
)
1320 struct WinDesc
*newwin
;
1324 if (maxwin
== MAXWIN
)
1327 newwin
= (struct WinDesc
*) alloc(sizeof(struct WinDesc
));
1328 newwin
->type
= type
;
1330 newwin
->active
= FALSE
;
1331 newwin
->curx
= newwin
->cury
= 0;
1332 newwin
->morestr
= 0;
1333 newwin
->mlist
= (tty_menu_item
*) 0;
1334 newwin
->plist
= (tty_menu_item
**) 0;
1335 newwin
->npages
= newwin
->plist_size
= newwin
->nitems
= newwin
->how
= 0;
1338 /* base window, used for absolute movement on the screen */
1339 newwin
->offx
= newwin
->offy
= 0;
1340 newwin
->rows
= ttyDisplay
->rows
;
1341 newwin
->cols
= ttyDisplay
->cols
;
1342 newwin
->maxrow
= newwin
->maxcol
= 0;
1345 /* message window, 1 line long, very wide, top of screen */
1346 newwin
->offx
= newwin
->offy
= 0;
1348 if (iflags
.msg_history
< 20)
1349 iflags
.msg_history
= 20;
1350 else if (iflags
.msg_history
> 60)
1351 iflags
.msg_history
= 60;
1352 newwin
->maxrow
= newwin
->rows
= iflags
.msg_history
;
1353 newwin
->maxcol
= newwin
->cols
= 0;
1356 /* status window, 2 lines long, full width, bottom of screen */
1358 #if defined(USE_TILES) && defined(MSDOS)
1359 if (iflags
.grmode
) {
1360 newwin
->offy
= ttyDisplay
->rows
- 2;
1363 newwin
->offy
= min((int) ttyDisplay
->rows
- 2, ROWNO
+ 1);
1364 newwin
->rows
= newwin
->maxrow
= 2;
1365 newwin
->cols
= newwin
->maxcol
= ttyDisplay
->cols
;
1368 /* map window, ROWNO lines long, full width, below message window */
1371 newwin
->rows
= ROWNO
;
1372 newwin
->cols
= COLNO
;
1373 newwin
->maxrow
= 0; /* no buffering done -- let gbuf do it */
1378 /* inventory/menu window, variable length, full width, top of screen
1380 /* help window, the same, different semantics for display, etc */
1381 newwin
->offx
= newwin
->offy
= 0;
1383 newwin
->cols
= ttyDisplay
->cols
;
1384 newwin
->maxrow
= newwin
->maxcol
= 0;
1387 panic("Tried to create window type %d\n", (int) type
);
1391 for (newid
= 0; newid
< MAXWIN
; newid
++) {
1392 if (wins
[newid
] == 0) {
1393 wins
[newid
] = newwin
;
1397 if (newid
== MAXWIN
) {
1398 panic("No window slots!");
1402 if (newwin
->maxrow
) {
1404 (char **) alloc(sizeof(char *) * (unsigned) newwin
->maxrow
);
1406 (short *) alloc(sizeof(short) * (unsigned) newwin
->maxrow
);
1407 if (newwin
->maxcol
) {
1409 for (i
= 0; i
< newwin
->maxrow
; i
++) {
1410 newwin
->data
[i
] = (char *) alloc((unsigned) newwin
->maxcol
);
1411 newwin
->datlen
[i
] = (short) newwin
->maxcol
;
1414 for (i
= 0; i
< newwin
->maxrow
; i
++) {
1415 newwin
->data
[i
] = (char *) 0;
1416 newwin
->datlen
[i
] = 0;
1419 if (newwin
->type
== NHW_MESSAGE
)
1422 newwin
->data
= (char **) 0;
1423 newwin
->datlen
= (short *) 0;
1430 erase_menu_or_text(window
, cw
, clear
)
1437 tty_curs(window
, 1, 0);
1444 docorner((int) cw
->offx
, cw
->maxrow
+ 1);
1448 free_window_info(cw
, free_data
)
1455 if (cw
== wins
[WIN_MESSAGE
] && cw
->rows
> cw
->maxrow
)
1456 cw
->maxrow
= cw
->rows
; /* topl data */
1457 for (i
= 0; i
< cw
->maxrow
; i
++)
1459 free((genericptr_t
) cw
->data
[i
]);
1460 cw
->data
[i
] = (char *) 0;
1465 free((genericptr_t
) cw
->data
);
1466 cw
->data
= (char **) 0;
1468 free((genericptr_t
) cw
->datlen
);
1469 cw
->datlen
= (short *) 0;
1473 cw
->maxrow
= cw
->maxcol
= 0;
1475 tty_menu_item
*temp
;
1477 while ((temp
= cw
->mlist
) != 0) {
1478 cw
->mlist
= cw
->mlist
->next
;
1480 free((genericptr_t
) temp
->str
);
1481 free((genericptr_t
) temp
);
1485 free((genericptr_t
) cw
->plist
);
1488 cw
->plist_size
= cw
->npages
= cw
->nitems
= cw
->how
= 0;
1490 free((genericptr_t
) cw
->morestr
);
1496 tty_clear_nhwindow(window
)
1499 register struct WinDesc
*cw
= 0;
1501 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0)
1502 panic(winpanicstr
, window
);
1503 ttyDisplay
->lastwin
= window
;
1505 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
1509 if (ttyDisplay
->toplin
) {
1513 docorner(1, cw
->cury
+ 1);
1514 ttyDisplay
->toplin
= 0;
1518 tty_curs(window
, 1, 0);
1520 tty_curs(window
, 1, 1);
1524 /* cheap -- clear the whole thing and tell nethack to redraw botl */
1533 erase_menu_or_text(window
, cw
, TRUE
);
1534 free_window_info(cw
, FALSE
);
1537 cw
->curx
= cw
->cury
= 0;
1541 toggle_menu_curr(window
, curr
, lineno
, in_view
, counting
, count
)
1543 tty_menu_item
*curr
;
1545 boolean in_view
, counting
;
1548 if (curr
->selected
) {
1549 if (counting
&& count
> 0) {
1550 curr
->count
= count
;
1552 set_item_state(window
, lineno
, curr
);
1554 } else { /* change state */
1555 curr
->selected
= FALSE
;
1558 set_item_state(window
, lineno
, curr
);
1561 } else { /* !selected */
1562 if (counting
&& count
> 0) {
1563 curr
->count
= count
;
1564 curr
->selected
= TRUE
;
1566 set_item_state(window
, lineno
, curr
);
1568 } else if (!counting
) {
1569 curr
->selected
= TRUE
;
1571 set_item_state(window
, lineno
, curr
);
1574 /* do nothing counting&&count==0 */
1581 register struct WinDesc
*cw
;
1582 const char *s
; /* valid responses */
1584 const char *prompt
= cw
->morestr
? cw
->morestr
: defmorestr
;
1585 int offset
= (cw
->type
== NHW_TEXT
) ? 1 : 2;
1587 tty_curs(BASE_WINDOW
, (int) ttyDisplay
->curx
+ offset
,
1588 (int) ttyDisplay
->cury
);
1592 ttyDisplay
->curx
+= strlen(prompt
);
1600 set_item_state(window
, lineno
, item
)
1603 tty_menu_item
*item
;
1605 char ch
= item
->selected
? (item
->count
== -1L ? '+' : '#') : '-';
1607 tty_curs(window
, 4, lineno
);
1608 term_start_attr(item
->attr
);
1611 term_end_attr(item
->attr
);
1615 set_all_on_page(window
, page_start
, page_end
)
1617 tty_menu_item
*page_start
, *page_end
;
1619 tty_menu_item
*curr
;
1622 for (n
= 0, curr
= page_start
; curr
!= page_end
; n
++, curr
= curr
->next
)
1623 if (curr
->identifier
.a_void
&& !curr
->selected
) {
1624 curr
->selected
= TRUE
;
1625 set_item_state(window
, n
, curr
);
1630 unset_all_on_page(window
, page_start
, page_end
)
1632 tty_menu_item
*page_start
, *page_end
;
1634 tty_menu_item
*curr
;
1637 for (n
= 0, curr
= page_start
; curr
!= page_end
; n
++, curr
= curr
->next
)
1638 if (curr
->identifier
.a_void
&& curr
->selected
) {
1639 curr
->selected
= FALSE
;
1641 set_item_state(window
, n
, curr
);
1646 invert_all_on_page(window
, page_start
, page_end
, acc
)
1648 tty_menu_item
*page_start
, *page_end
;
1649 char acc
; /* group accelerator, 0 => all */
1651 tty_menu_item
*curr
;
1654 for (n
= 0, curr
= page_start
; curr
!= page_end
; n
++, curr
= curr
->next
)
1655 if (curr
->identifier
.a_void
&& (acc
== 0 || curr
->gselector
== acc
)) {
1656 if (curr
->selected
) {
1657 curr
->selected
= FALSE
;
1660 curr
->selected
= TRUE
;
1661 set_item_state(window
, n
, curr
);
1666 * Invert all entries that match the give group accelerator (or all if zero).
1669 invert_all(window
, page_start
, page_end
, acc
)
1671 tty_menu_item
*page_start
, *page_end
;
1672 char acc
; /* group accelerator, 0 => all */
1674 tty_menu_item
*curr
;
1675 boolean on_curr_page
;
1676 struct WinDesc
*cw
= wins
[window
];
1678 invert_all_on_page(window
, page_start
, page_end
, acc
);
1680 /* invert the rest */
1681 for (on_curr_page
= FALSE
, curr
= cw
->mlist
; curr
; curr
= curr
->next
) {
1682 if (curr
== page_start
)
1683 on_curr_page
= TRUE
;
1684 else if (curr
== page_end
)
1685 on_curr_page
= FALSE
;
1687 if (!on_curr_page
&& curr
->identifier
.a_void
1688 && (acc
== 0 || curr
->gselector
== acc
)) {
1689 if (curr
->selected
) {
1690 curr
->selected
= FALSE
;
1693 curr
->selected
= TRUE
;
1698 /* support menucolor in addition to caller-supplied attribute */
1700 toggle_menu_attr(on
, color
, attr
)
1705 term_start_attr(attr
);
1707 if (color
!= NO_COLOR
)
1708 term_start_color(color
);
1712 if (color
!= NO_COLOR
)
1715 term_end_attr(attr
);
1724 process_menu_window(window
, cw
)
1728 tty_menu_item
*page_start
, *page_end
, *curr
;
1730 int n
, attr_n
, curr_page
, page_lines
, resp_len
;
1731 boolean finished
, counting
, reset_count
;
1732 char *cp
, *rp
, resp
[QBUFSZ
], gacc
[QBUFSZ
], *msave
, *morestr
, really_morc
;
1733 #define MENU_EXPLICIT_CHOICE 0x7f /* pseudo menu manipulation char */
1735 curr_page
= page_lines
= 0;
1736 page_start
= page_end
= 0;
1737 msave
= cw
->morestr
; /* save the morestr */
1738 cw
->morestr
= morestr
= (char *) alloc((unsigned) QBUFSZ
);
1744 /* collect group accelerators; for PICK_NONE, they're ignored;
1745 for PICK_ONE, only those which match exactly one entry will be
1746 accepted; for PICK_ANY, those which match any entry are okay */
1748 if (cw
->how
!= PICK_NONE
) {
1750 #define GSELIDX(c) (c & 127) /* guard against `signed char' */
1752 for (i
= 0; i
< SIZE(gcnt
); i
++)
1754 for (n
= 0, curr
= cw
->mlist
; curr
; curr
= curr
->next
)
1755 if (curr
->gselector
&& curr
->gselector
!= curr
->selector
) {
1757 ++gcnt
[GSELIDX(curr
->gselector
)];
1760 if (n
> 0) /* at least one group accelerator found */
1761 for (rp
= gacc
, curr
= cw
->mlist
; curr
; curr
= curr
->next
)
1762 if (curr
->gselector
&& curr
->gselector
!= curr
->selector
1763 && !index(gacc
, curr
->gselector
)
1764 && (cw
->how
== PICK_ANY
1765 || gcnt
[GSELIDX(curr
->gselector
)] == 1)) {
1766 *rp
++ = curr
->gselector
;
1767 *rp
= '\0'; /* re-terminate for index() */
1770 resp_len
= 0; /* lint suppression */
1772 /* loop until finished */
1781 /* new page to be displayed */
1782 if (curr_page
< 0 || (cw
->npages
> 0 && curr_page
>= cw
->npages
))
1783 panic("bad menu screen page #%d", curr_page
);
1786 if (!cw
->offx
) { /* if not corner, do clearscreen */
1788 tty_curs(window
, 1, 0);
1795 if (cw
->npages
> 0) {
1796 /* collect accelerators */
1797 page_start
= cw
->plist
[curr_page
];
1798 page_end
= cw
->plist
[curr_page
+ 1];
1799 for (page_lines
= 0, curr
= page_start
; curr
!= page_end
;
1800 page_lines
++, curr
= curr
->next
) {
1801 int attr
, color
= NO_COLOR
;
1804 *rp
++ = curr
->selector
;
1806 tty_curs(window
, 1, page_lines
);
1810 (void) putchar(' ');
1813 if (!iflags
.use_menu_color
1814 || !get_menu_coloring(curr
->str
, &color
, &attr
))
1817 /* which character to start attribute highlighting;
1818 whole line for headers and such, after the selector
1819 character and space and selection indicator for menu
1820 lines (including fake ones that simulate grayed-out
1821 entries, so we don't rely on curr->identifier here) */
1822 attr_n
= 0; /* whole line */
1823 if (curr
->str
[0] && curr
->str
[1] == ' '
1824 && curr
->str
[2] && index("-+#", curr
->str
[2])
1825 && curr
->str
[3] == ' ')
1826 /* [0]=letter, [1]==space, [2]=[-+#], [3]=space */
1827 attr_n
= 4; /* [4:N]=entry description */
1830 * Don't use xputs() because (1) under unix it calls
1831 * tputstr() which will interpret a '*' as some kind
1832 * of padding information and (2) it calls xputc to
1833 * actually output the character. We're faster doing
1836 for (n
= 0, cp
= curr
->str
;
1839 (int) ++ttyDisplay
->curx
< (int) ttyDisplay
->cols
;
1841 (int) ttyDisplay
->curx
< (int) ttyDisplay
->cols
;
1845 if (n
== attr_n
&& (color
!= NO_COLOR
1846 || attr
!= ATR_NONE
))
1847 toggle_menu_attr(TRUE
, color
, attr
);
1849 && curr
->identifier
.a_void
!= 0
1850 && curr
->selected
) {
1851 if (curr
->count
== -1L)
1852 (void) putchar('+'); /* all selected */
1854 (void) putchar('#'); /* count selected */
1856 (void) putchar(*cp
);
1858 if (n
> attr_n
&& (color
!= NO_COLOR
|| attr
!= ATR_NONE
))
1859 toggle_menu_attr(FALSE
, color
, attr
);
1860 } /* if npages > 0 */
1867 /* remember how many explicit menu choices there are */
1868 resp_len
= (int) strlen(resp
);
1870 /* corner window - clear extra lines from last page */
1872 for (n
= page_lines
+ 1; n
< cw
->maxrow
; n
++) {
1873 tty_curs(window
, 1, n
);
1878 /* set extra chars.. */
1879 Strcat(resp
, default_menu_cmds
);
1880 Strcat(resp
, " "); /* next page or end */
1881 Strcat(resp
, "0123456789\033\n\r"); /* counts, quit */
1882 Strcat(resp
, gacc
); /* group accelerators */
1883 Strcat(resp
, mapped_menu_cmds
);
1886 Sprintf(cw
->morestr
, "(%d of %d)", curr_page
+ 1,
1889 Strcpy(cw
->morestr
, msave
);
1891 Strcpy(cw
->morestr
, defmorestr
);
1893 tty_curs(window
, 1, page_lines
);
1897 /* just put the cursor back... */
1898 tty_curs(window
, (int) strlen(cw
->morestr
) + 2, page_lines
);
1899 xwaitforspace(resp
);
1902 really_morc
= morc
; /* (only used with MENU_EXPLICIT_CHOICE */
1903 if ((rp
= index(resp
, morc
)) != 0 && rp
< resp
+ resp_len
)
1904 /* explicit menu selection; don't override it if it also
1905 happens to match a mapped menu command (such as ':' to
1906 look inside a container vs ':' to search) */
1907 morc
= MENU_EXPLICIT_CHOICE
;
1909 morc
= map_menu_cmd(morc
);
1913 /* special case: '0' is also the default ball class */
1914 if (!counting
&& index(gacc
, morc
))
1916 /* fall through to count the zero */
1926 count
= (count
* 10L) + (long) (morc
- '0');
1928 * It is debatable whether we should allow 0 to
1929 * start a count. There is no difference if the
1930 * item is selected. If not selected, then
1933 * count starting zero: "zero b's"
1934 * ignore starting zero: "select b"
1936 * At present I don't know which is better.
1938 if (count
!= 0L) { /* ignore leading zeros */
1940 reset_count
= FALSE
;
1943 case '\033': /* cancel - from counting or loop */
1945 /* deselect everything */
1946 for (curr
= cw
->mlist
; curr
; curr
= curr
->next
) {
1947 curr
->selected
= FALSE
;
1950 cw
->flags
|= WIN_CANCELLED
;
1953 /* else only stop count */
1955 case '\0': /* finished (commit) */
1958 /* only finished if we are actually picking something */
1959 if (cw
->how
!= PICK_NONE
) {
1963 /* else fall through */
1965 case MENU_NEXT_PAGE
:
1966 if (cw
->npages
> 0 && curr_page
!= cw
->npages
- 1) {
1969 } else if (morc
== ' ') {
1970 /* ' ' finishes menus here, but stop '>' doing the same. */
1974 case MENU_PREVIOUS_PAGE
:
1975 if (cw
->npages
> 0 && curr_page
!= 0) {
1980 case MENU_FIRST_PAGE
:
1981 if (cw
->npages
> 0 && curr_page
!= 0) {
1986 case MENU_LAST_PAGE
:
1987 if (cw
->npages
> 0 && curr_page
!= cw
->npages
- 1) {
1989 curr_page
= cw
->npages
- 1;
1992 case MENU_SELECT_PAGE
:
1993 if (cw
->how
== PICK_ANY
)
1994 set_all_on_page(window
, page_start
, page_end
);
1996 case MENU_UNSELECT_PAGE
:
1997 unset_all_on_page(window
, page_start
, page_end
);
1999 case MENU_INVERT_PAGE
:
2000 if (cw
->how
== PICK_ANY
)
2001 invert_all_on_page(window
, page_start
, page_end
, 0);
2003 case MENU_SELECT_ALL
:
2004 if (cw
->how
== PICK_ANY
) {
2005 set_all_on_page(window
, page_start
, page_end
);
2007 for (curr
= cw
->mlist
; curr
; curr
= curr
->next
)
2008 if (curr
->identifier
.a_void
&& !curr
->selected
)
2009 curr
->selected
= TRUE
;
2012 case MENU_UNSELECT_ALL
:
2013 unset_all_on_page(window
, page_start
, page_end
);
2014 /* unset the rest */
2015 for (curr
= cw
->mlist
; curr
; curr
= curr
->next
)
2016 if (curr
->identifier
.a_void
&& curr
->selected
) {
2017 curr
->selected
= FALSE
;
2021 case MENU_INVERT_ALL
:
2022 if (cw
->how
== PICK_ANY
)
2023 invert_all(window
, page_start
, page_end
, 0);
2026 if (cw
->how
== PICK_NONE
) {
2030 char searchbuf
[BUFSZ
+ 2], tmpbuf
[BUFSZ
];
2031 boolean on_curr_page
= FALSE
;
2034 tty_getlin("Search for:", tmpbuf
);
2035 if (!tmpbuf
[0] || tmpbuf
[0] == '\033')
2037 Sprintf(searchbuf
, "*%s*", tmpbuf
);
2039 for (curr
= cw
->mlist
; curr
; curr
= curr
->next
) {
2042 if (curr
== page_start
)
2043 on_curr_page
= TRUE
;
2044 else if (curr
== page_end
)
2045 on_curr_page
= FALSE
;
2046 if (curr
->identifier
.a_void
2047 && pmatchi(searchbuf
, curr
->str
)) {
2048 toggle_menu_curr(window
, curr
, lineno
, on_curr_page
,
2050 if (cw
->how
== PICK_ONE
) {
2058 case MENU_EXPLICIT_CHOICE
:
2062 if (cw
->how
== PICK_NONE
|| !index(resp
, morc
)) {
2063 /* unacceptable input received */
2066 } else if (index(gacc
, morc
)) {
2068 /* group accelerator; for the PICK_ONE case, we know that
2069 it matches exactly one item in order to be in gacc[] */
2070 invert_all(window
, page_start
, page_end
, morc
);
2071 if (cw
->how
== PICK_ONE
)
2075 /* find, toggle, and possibly update */
2076 for (n
= 0, curr
= page_start
; curr
!= page_end
;
2077 n
++, curr
= curr
->next
)
2078 if (morc
== curr
->selector
) {
2079 toggle_menu_curr(window
, curr
, n
, TRUE
, counting
, count
);
2080 if (cw
->how
== PICK_ONE
)
2082 break; /* from `for' loop */
2088 cw
->morestr
= msave
;
2089 free((genericptr_t
) morestr
);
2093 process_text_window(window
, cw
)
2100 for (n
= 0, i
= 0; i
< cw
->maxrow
; i
++) {
2101 if (!cw
->offx
&& (n
+ cw
->offy
== ttyDisplay
->rows
- 1)) {
2102 tty_curs(window
, 1, n
);
2104 dmore(cw
, quitchars
);
2105 if (morc
== '\033') {
2106 cw
->flags
|= WIN_CANCELLED
;
2110 tty_curs(window
, 1, 0);
2116 tty_curs(window
, 1, n
++);
2124 attr
= cw
->data
[i
][0] - 1;
2126 (void) putchar(' ');
2129 term_start_attr(attr
);
2130 for (cp
= &cw
->data
[i
][1];
2132 *cp
&& (int) ++ttyDisplay
->curx
< (int) ttyDisplay
->cols
;
2135 *cp
&& (int) ttyDisplay
->curx
< (int) ttyDisplay
->cols
;
2136 cp
++, ttyDisplay
->curx
++)
2138 (void) putchar(*cp
);
2139 term_end_attr(attr
);
2142 if (i
== cw
->maxrow
) {
2144 if (cw
->type
== NHW_TEXT
) {
2145 tty_curs(BASE_WINDOW
, 1, (int) ttyDisplay
->cury
+ 1);
2149 tty_curs(BASE_WINDOW
, (int) cw
->offx
+ 1,
2150 (cw
->type
== NHW_TEXT
) ? (int) ttyDisplay
->rows
- 1 : n
);
2152 dmore(cw
, quitchars
);
2154 cw
->flags
|= WIN_CANCELLED
;
2160 tty_display_nhwindow(window
, blocking
)
2162 boolean blocking
; /* with ttys, all windows are blocking */
2164 register struct WinDesc
*cw
= 0;
2167 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0)
2168 panic(winpanicstr
, window
);
2169 if (cw
->flags
& WIN_CANCELLED
)
2171 ttyDisplay
->lastwin
= window
;
2172 ttyDisplay
->rawprint
= 0;
2174 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
2178 if (ttyDisplay
->toplin
== 1) {
2180 ttyDisplay
->toplin
= 1; /* more resets this */
2181 tty_clear_nhwindow(window
);
2183 ttyDisplay
->toplin
= 0;
2184 cw
->curx
= cw
->cury
= 0;
2186 iflags
.window_inited
= TRUE
;
2191 if (!ttyDisplay
->toplin
)
2192 ttyDisplay
->toplin
= 1;
2193 tty_display_nhwindow(WIN_MESSAGE
, TRUE
);
2197 (void) fflush(stdout
);
2200 cw
->maxcol
= ttyDisplay
->cols
; /* force full-screen mode */
2204 /* cw->maxcol is a long, but its value is constrained to
2205 be <= ttyDisplay->cols, so is sure to fit within a short */
2206 s_maxcol
= (short) cw
->maxcol
;
2208 cw
->offx
= (cw
->type
== NHW_TEXT
)
2210 : min(min(82, ttyDisplay
->cols
/ 2),
2211 ttyDisplay
->cols
- s_maxcol
- 1);
2213 /* avoid converting to uchar before calculations are finished */
2214 cw
->offx
= (uchar
) max((int) 10,
2215 (int) (ttyDisplay
->cols
- s_maxcol
- 1));
2219 if (cw
->type
== NHW_MENU
)
2221 if (ttyDisplay
->toplin
== 1)
2222 tty_display_nhwindow(WIN_MESSAGE
, TRUE
);
2224 if (cw
->maxrow
>= (int) ttyDisplay
->rows
2225 || !iflags
.menu_overlay
)
2227 if (cw
->offx
== 10 || cw
->maxrow
>= (int) ttyDisplay
->rows
2228 || !iflags
.menu_overlay
)
2232 if (cw
->offy
|| iflags
.menu_overlay
) {
2233 tty_curs(window
, 1, 0);
2237 ttyDisplay
->toplin
= 0;
2239 if (WIN_MESSAGE
!= WIN_ERR
)
2240 tty_clear_nhwindow(WIN_MESSAGE
);
2243 if (cw
->data
|| !cw
->maxrow
)
2244 process_text_window(window
, cw
);
2246 process_menu_window(window
, cw
);
2253 tty_dismiss_nhwindow(window
)
2256 register struct WinDesc
*cw
= 0;
2258 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0)
2259 panic(winpanicstr
, window
);
2261 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
2265 if (ttyDisplay
->toplin
)
2266 tty_display_nhwindow(WIN_MESSAGE
, TRUE
);
2272 * these should only get dismissed when the game is going away
2275 tty_curs(BASE_WINDOW
, 1, (int) ttyDisplay
->rows
- 1);
2281 if (iflags
.window_inited
) {
2282 /* otherwise dismissing the text endwin after other windows
2283 * are dismissed tries to redraw the map and panics. since
2284 * the whole reason for dismissing the other windows was to
2285 * leave the ending window on the screen, we don't want to
2288 erase_menu_or_text(window
, cw
, FALSE
);
2298 tty_destroy_nhwindow(window
)
2301 register struct WinDesc
*cw
= 0;
2303 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0)
2304 panic(winpanicstr
, window
);
2307 tty_dismiss_nhwindow(window
);
2308 if (cw
->type
== NHW_MESSAGE
)
2309 iflags
.window_inited
= 0;
2310 if (cw
->type
== NHW_MAP
)
2313 free_window_info(cw
, TRUE
);
2314 free((genericptr_t
) cw
);
2319 tty_curs(window
, x
, y
)
2321 register int x
, y
; /* not xchar: perhaps xchar is unsigned and
2322 curx-x would be unsigned as well */
2324 struct WinDesc
*cw
= 0;
2325 int cx
= ttyDisplay
->curx
;
2326 int cy
= ttyDisplay
->cury
;
2328 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0)
2329 panic(winpanicstr
, window
);
2330 ttyDisplay
->lastwin
= window
;
2332 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
2334 #if defined(USE_TILES) && defined(MSDOS)
2335 adjust_cursor_flags(cw
);
2337 cw
->curx
= --x
; /* column 0 is never used */
2340 if (x
< 0 || y
< 0 || y
>= cw
->rows
|| x
> cw
->cols
) {
2341 const char *s
= "[unknown type]";
2344 s
= "[topl window]";
2347 s
= "[status window]";
2353 s
= "[corner window]";
2356 s
= "[text window]";
2359 s
= "[base window]";
2362 debugpline4("bad curs positioning win %d %s (%d,%d)", window
, s
, x
,
2371 if (clipping
&& window
== WIN_MAP
) {
2377 if (y
== cy
&& x
== cx
)
2380 if (cw
->type
== NHW_MAP
)
2384 if (!nh_ND
&& (cx
!= x
|| x
<= 3)) { /* Extremely primitive */
2385 cmov(x
, y
); /* bunker!wtm */
2394 if (cy
<= 3 && cx
<= 3) {
2397 } else if ((x
<= 3 && cy
<= 3) || (!nh_CM
&& x
< cx
)) {
2398 (void) putchar('\r');
2399 ttyDisplay
->curx
= 0;
2401 } else if (!nh_CM
) {
2407 ttyDisplay
->curx
= x
;
2408 ttyDisplay
->cury
= y
;
2412 tty_putsym(window
, x
, y
, ch
)
2417 register struct WinDesc
*cw
= 0;
2419 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0)
2420 panic(winpanicstr
, window
);
2422 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
2428 tty_curs(window
, x
, y
);
2436 impossible("Can't putsym to window type %d", cw
->type
);
2441 STATIC_OVL
const char *
2445 static char cbuf
[BUFSZ
];
2446 /* compress in case line too long */
2447 if ((int) strlen(str
) >= CO
) {
2448 register const char *bp0
= str
;
2449 register char *bp1
= cbuf
;
2453 if (*bp0
!= ' ' || bp0
[1] != ' ')
2455 if (*bp0
!= ' ' || bp0
[1] != ' ' || bp0
[2] != ' ')
2465 tty_putstr(window
, attr
, str
)
2470 register struct WinDesc
*cw
= 0;
2472 register const char *nb
;
2473 register long i
, j
, n0
;
2475 /* Assume there's a real problem if the window is missing --
2476 * probably a panic message
2478 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0) {
2483 if (str
== (const char *) 0
2484 || ((cw
->flags
& WIN_CANCELLED
) && (cw
->type
!= NHW_MESSAGE
)))
2486 if (cw
->type
!= NHW_MESSAGE
)
2487 str
= compress_str(str
);
2489 ttyDisplay
->lastwin
= window
;
2491 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
2495 /* really do this later */
2496 #if defined(USER_SOUNDS) && defined(WIN32CON)
2497 play_sound_for_message(str
);
2503 ob
= &cw
->data
[cw
->cury
][j
= cw
->curx
];
2506 if (!cw
->cury
&& (int) strlen(str
) >= CO
) {
2507 /* the characters before "St:" are unnecessary */
2508 nb
= index(str
, ':');
2509 if (nb
&& nb
> str
+ 2)
2513 for (i
= cw
->curx
+ 1, n0
= cw
->cols
; i
< n0
; i
++, nb
++) {
2515 if (*ob
|| context
.botlx
) {
2516 /* last char printed may be in middle of line */
2517 tty_curs(WIN_STATUS
, i
, cw
->cury
);
2523 tty_putsym(WIN_STATUS
, i
, cw
->cury
, *nb
);
2528 (void) strncpy(&cw
->data
[cw
->cury
][j
], str
, cw
->cols
- j
- 1);
2529 cw
->data
[cw
->cury
][cw
->cols
- 1] = '\0'; /* null terminate */
2530 #ifdef STATUS_VIA_WINDOWPORT
2531 if (!iflags
.use_status_hilites
) {
2533 cw
->cury
= (cw
->cury
+ 1) % 2;
2535 #ifdef STATUS_VIA_WINDOWPORT
2540 tty_curs(window
, cw
->curx
+ 1, cw
->cury
);
2541 term_start_attr(attr
);
2542 while (*str
&& (int) ttyDisplay
->curx
< (int) ttyDisplay
->cols
- 1) {
2543 (void) putchar(*str
);
2549 term_end_attr(attr
);
2552 tty_curs(window
, cw
->curx
+ 1, cw
->cury
);
2553 term_start_attr(attr
);
2555 if ((int) ttyDisplay
->curx
>= (int) ttyDisplay
->cols
- 1) {
2558 tty_curs(window
, cw
->curx
+ 1, cw
->cury
);
2560 (void) putchar(*str
);
2566 term_end_attr(attr
);
2571 if (cw
->type
== NHW_TEXT
2572 && (cw
->cury
+ cw
->offy
) == ttyDisplay
->rows
- 1)
2574 if (cw
->type
== NHW_TEXT
&& cw
->cury
== ttyDisplay
->rows
- 1)
2577 /* not a menu, so save memory and output 1 page at a time */
2578 cw
->maxcol
= ttyDisplay
->cols
; /* force full-screen mode */
2579 tty_display_nhwindow(window
, TRUE
);
2580 for (i
= 0; i
< cw
->maxrow
; i
++)
2582 free((genericptr_t
) cw
->data
[i
]);
2585 cw
->maxrow
= cw
->cury
= 0;
2587 /* always grows one at a time, but alloc 12 at a time */
2588 if (cw
->cury
>= cw
->rows
) {
2592 tmp
= (char **) alloc(sizeof(char *) * (unsigned) cw
->rows
);
2593 for (i
= 0; i
< cw
->maxrow
; i
++)
2594 tmp
[i
] = cw
->data
[i
];
2596 free((genericptr_t
) cw
->data
);
2599 for (i
= cw
->maxrow
; i
< cw
->rows
; i
++)
2602 if (cw
->data
[cw
->cury
])
2603 free((genericptr_t
) cw
->data
[cw
->cury
]);
2604 n0
= strlen(str
) + 1;
2605 ob
= cw
->data
[cw
->cury
] = (char *) alloc((unsigned) n0
+ 1);
2606 *ob
++ = (char) (attr
+ 1); /* avoid nuls, for convenience */
2609 if (n0
> cw
->maxcol
)
2611 if (++cw
->cury
> cw
->maxrow
)
2612 cw
->maxrow
= cw
->cury
;
2614 /* attempt to break the line */
2615 for (i
= CO
- 1; i
&& str
[i
] != ' ' && str
[i
] != '\n';)
2618 cw
->data
[cw
->cury
- 1][++i
] = '\0';
2619 tty_putstr(window
, attr
, &str
[i
]);
2627 tty_display_file(fname
, complain
)
2631 #ifdef DEF_PAGER /* this implies that UNIX is defined */
2633 /* use external pager; this may give security problems */
2634 register int fd
= open(fname
, 0);
2638 pline("Cannot open %s.", fname
);
2644 /* Now that child() does a setuid(getuid()) and a chdir(),
2645 we may not be able to open file fname anymore, so make
2650 raw_printf("Cannot open %s as stdin.", fname
);
2652 (void) execlp(catmore
, "page", (char *) 0);
2654 raw_printf("Cannot exec %s.", catmore
);
2657 sleep(10); /* want to wait_synch() but stdin is gone */
2658 terminate(EXIT_FAILURE
);
2665 #else /* DEF_PAGER */
2671 tty_clear_nhwindow(WIN_MESSAGE
);
2672 f
= dlb_fopen(fname
, "r");
2680 pline("Cannot open \"%s\".", fname
);
2684 winid datawin
= tty_create_nhwindow(NHW_TEXT
);
2685 boolean empty
= TRUE
;
2692 /* attempt to scroll text below map window if there's room */
2693 wins
[datawin
]->offy
= wins
[WIN_STATUS
]->offy
+ 3;
2694 if ((int) wins
[datawin
]->offy
+ 12 > (int) ttyDisplay
->rows
)
2695 wins
[datawin
]->offy
= 0;
2697 while (dlb_fgets(buf
, BUFSZ
, f
)) {
2698 if ((cr
= index(buf
, '\n')) != 0)
2701 if ((cr
= index(buf
, '\r')) != 0)
2704 if (index(buf
, '\t') != 0)
2705 (void) tabexpand(buf
);
2707 tty_putstr(datawin
, 0, buf
);
2708 if (wins
[datawin
]->flags
& WIN_CANCELLED
)
2712 tty_display_nhwindow(datawin
, FALSE
);
2713 tty_destroy_nhwindow(datawin
);
2714 (void) dlb_fclose(f
);
2717 #endif /* DEF_PAGER */
2721 tty_start_menu(window
)
2724 tty_clear_nhwindow(window
);
2730 * Add a menu item to the beginning of the menu list. This list is reversed
2734 tty_add_menu(window
, glyph
, identifier
, ch
, gch
, attr
, str
, preselected
)
2735 winid window
; /* window to use, must be of type NHW_MENU */
2736 int glyph UNUSED
; /* glyph to display with item (not used) */
2737 const anything
*identifier
; /* what to return if selected */
2738 char ch
; /* keyboard accelerator (0 = pick our own) */
2739 char gch
; /* group accelerator (0 = no group) */
2740 int attr
; /* attribute for string (like tty_putstr()) */
2741 const char *str
; /* menu string */
2742 boolean preselected
; /* item is marked as selected */
2744 register struct WinDesc
*cw
= 0;
2745 tty_menu_item
*item
;
2747 char buf
[4 + BUFSZ
];
2749 if (str
== (const char *) 0)
2752 if (window
== WIN_ERR
2753 || (cw
= wins
[window
]) == (struct WinDesc
*) 0
2754 || cw
->type
!= NHW_MENU
)
2755 panic(winpanicstr
, window
);
2758 if (identifier
->a_void
) {
2759 int len
= strlen(str
);
2762 /* We *think* everything's coming in off at most BUFSZ bufs... */
2763 impossible("Menu item too long (%d).", len
);
2766 Sprintf(buf
, "%c - ", ch
? ch
: '?');
2767 (void) strncpy(buf
+ 4, str
, len
);
2768 buf
[4 + len
] = '\0';
2773 item
= (tty_menu_item
*) alloc(sizeof(tty_menu_item
));
2774 item
->identifier
= *identifier
;
2776 item
->selected
= preselected
;
2777 item
->selector
= ch
;
2778 item
->gselector
= gch
;
2780 item
->str
= dupstr(newstr
? newstr
: "");
2782 item
->next
= cw
->mlist
;
2786 /* Invert the given list, can handle NULL as an input. */
2787 STATIC_OVL tty_menu_item
*
2789 tty_menu_item
*curr
;
2791 tty_menu_item
*next
, *head
= 0;
2803 * End a menu in this window, window must a type NHW_MENU. This routine
2804 * processes the string list. We calculate the # of pages, then assign
2805 * keyboard accelerators as needed. Finally we decide on the width and
2806 * height of the window.
2809 tty_end_menu(window
, prompt
)
2810 winid window
; /* menu to use */
2811 const char *prompt
; /* prompt to for menu */
2813 struct WinDesc
*cw
= 0;
2814 tty_menu_item
*curr
;
2819 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0
2820 || cw
->type
!= NHW_MENU
)
2821 panic(winpanicstr
, window
);
2823 /* Reverse the list so that items are in correct order. */
2824 cw
->mlist
= reverse(cw
->mlist
);
2826 /* Put the prompt at the beginning of the menu. */
2830 any
= zeroany
; /* not selectable */
2831 tty_add_menu(window
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "",
2833 tty_add_menu(window
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, prompt
,
2837 /* XXX another magic number? 52 */
2838 lmax
= min(52, (int) ttyDisplay
->rows
- 1); /* # lines per page */
2839 cw
->npages
= (cw
->nitems
+ (lmax
- 1)) / lmax
; /* # of pages */
2841 /* make sure page list is large enough */
2842 if (cw
->plist_size
< cw
->npages
+ 1 /*need 1 slot beyond last*/) {
2844 free((genericptr_t
) cw
->plist
);
2845 cw
->plist_size
= cw
->npages
+ 1;
2846 cw
->plist
= (tty_menu_item
**) alloc(cw
->plist_size
2847 * sizeof(tty_menu_item
*));
2850 cw
->cols
= 0; /* cols is set when the win is initialized... (why?) */
2851 menu_ch
= '?'; /* lint suppression */
2852 for (n
= 0, curr
= cw
->mlist
; curr
; n
++, curr
= curr
->next
) {
2853 /* set page boundaries and character accelerators */
2854 if ((n
% lmax
) == 0) {
2856 cw
->plist
[n
/ lmax
] = curr
;
2858 if (curr
->identifier
.a_void
&& !curr
->selector
) {
2859 curr
->str
[0] = curr
->selector
= menu_ch
;
2860 if (menu_ch
++ == 'z')
2864 /* cut off any lines that are too long */
2865 len
= strlen(curr
->str
) + 2; /* extra space at beg & end */
2866 if (len
> (int) ttyDisplay
->cols
) {
2867 curr
->str
[ttyDisplay
->cols
- 2] = 0;
2868 len
= ttyDisplay
->cols
;
2873 cw
->plist
[cw
->npages
] = 0; /* plist terminator */
2876 * If greater than 1 page, morestr is "(x of y) " otherwise, "(end) "
2878 if (cw
->npages
> 1) {
2880 /* produce the largest demo string */
2881 Sprintf(buf
, "(%ld of %ld) ", cw
->npages
, cw
->npages
);
2883 cw
->morestr
= dupstr("");
2885 cw
->morestr
= dupstr("(end) ");
2886 len
= strlen(cw
->morestr
);
2889 if (len
> (int) ttyDisplay
->cols
) {
2890 /* truncate the prompt if it's too long for the screen */
2891 if (cw
->npages
<= 1) /* only str in single page case */
2892 cw
->morestr
[ttyDisplay
->cols
] = 0;
2893 len
= ttyDisplay
->cols
;
2898 cw
->maxcol
= cw
->cols
;
2901 * The number of lines in the first page plus the morestr will be the
2902 * maximum size of the window.
2905 cw
->maxrow
= cw
->rows
= lmax
+ 1;
2907 cw
->maxrow
= cw
->rows
= cw
->nitems
+ 1;
2911 tty_select_menu(window
, how
, menu_list
)
2914 menu_item
**menu_list
;
2916 register struct WinDesc
*cw
= 0;
2917 tty_menu_item
*curr
;
2921 if (window
== WIN_ERR
|| (cw
= wins
[window
]) == (struct WinDesc
*) 0
2922 || cw
->type
!= NHW_MENU
)
2923 panic(winpanicstr
, window
);
2925 *menu_list
= (menu_item
*) 0;
2926 cw
->how
= (short) how
;
2928 tty_display_nhwindow(window
, TRUE
);
2929 cancelled
= !!(cw
->flags
& WIN_CANCELLED
);
2930 tty_dismiss_nhwindow(window
); /* does not destroy window data */
2935 for (n
= 0, curr
= cw
->mlist
; curr
; curr
= curr
->next
)
2941 *menu_list
= (menu_item
*) alloc(n
* sizeof(menu_item
));
2942 for (mi
= *menu_list
, curr
= cw
->mlist
; curr
; curr
= curr
->next
)
2943 if (curr
->selected
) {
2944 mi
->item
= curr
->identifier
;
2945 mi
->count
= curr
->count
;
2953 /* special hack for treating top line --More-- as a one item menu */
2955 tty_message_menu(let
, how
, mesg
)
2960 /* "menu" without selection; use ordinary pline, no more() */
2961 if (how
== PICK_NONE
) {
2966 ttyDisplay
->dismiss_more
= let
;
2968 /* barebones pline(); since we're only supposed to be called after
2969 response to a prompt, we'll assume that the display is up to date */
2970 tty_putstr(WIN_MESSAGE
, 0, mesg
);
2971 /* if `mesg' didn't wrap (triggering --More--), force --More-- now */
2972 if (ttyDisplay
->toplin
== 1) {
2974 ttyDisplay
->toplin
= 1; /* more resets this */
2975 tty_clear_nhwindow(WIN_MESSAGE
);
2977 /* normally <ESC> means skip further messages, but in this case
2978 it means cancel the current prompt; any other messages should
2979 continue to be output normally */
2980 wins
[WIN_MESSAGE
]->flags
&= ~WIN_CANCELLED
;
2981 ttyDisplay
->dismiss_more
= 0;
2983 return ((how
== PICK_ONE
&& morc
== let
) || morc
== '\033') ? morc
: '\0';
2987 tty_update_inventory()
2995 (void) fflush(stdout
);
3001 /* we just need to make sure all windows are synch'd */
3002 if (!ttyDisplay
|| ttyDisplay
->rawprint
) {
3005 ttyDisplay
->rawprint
= 0;
3007 tty_display_nhwindow(WIN_MAP
, FALSE
);
3008 if (ttyDisplay
->inmore
) {
3009 addtopl("--More--");
3010 (void) fflush(stdout
);
3011 } else if (ttyDisplay
->inread
> program_state
.gameover
) {
3012 /* this can only happen if we were reading and got interrupted */
3013 ttyDisplay
->toplin
= 3;
3014 /* do this twice; 1st time gets the Quit? message again */
3015 (void) tty_doprev_message();
3016 (void) tty_doprev_message();
3018 (void) fflush(stdout
);
3024 docorner(xmin
, ymax
)
3025 register int xmin
, ymax
;
3028 register struct WinDesc
*cw
= wins
[WIN_MAP
];
3030 #if 0 /* this optimization is not valuable enough to justify
3031 abusing core internals... */
3032 if (u
.uswallow
) { /* Can be done more efficiently */
3034 /* without this flush, if we happen to follow --More-- displayed in
3035 leftmost column, the cursor gets left in the wrong place after
3036 <docorner<more<update_topl<tty_putstr calls unwind back to core */
3042 #if defined(SIGWINCH) && defined(CLIPPING)
3044 ymax
= LI
; /* can happen if window gets smaller */
3046 for (y
= 0; y
< ymax
; y
++) {
3047 tty_curs(BASE_WINDOW
, xmin
, y
); /* move cursor */
3048 cl_end(); /* clear to end of line */
3050 if (y
< (int) cw
->offy
|| y
+ clipy
> ROWNO
)
3051 continue; /* only refresh board */
3052 #if defined(USE_TILES) && defined(MSDOS)
3053 if (iflags
.tile_view
)
3054 row_refresh((xmin
/ 2) + clipx
- ((int) cw
->offx
/ 2), COLNO
- 1,
3055 y
+ clipy
- (int) cw
->offy
);
3058 row_refresh(xmin
+ clipx
- (int) cw
->offx
, COLNO
- 1,
3059 y
+ clipy
- (int) cw
->offy
);
3061 if (y
< cw
->offy
|| y
> ROWNO
)
3062 continue; /* only refresh board */
3063 row_refresh(xmin
- (int) cw
->offx
, COLNO
- 1, y
- (int) cw
->offy
);
3068 if (ymax
>= (int) wins
[WIN_STATUS
]->offy
) {
3069 /* we have wrecked the bottom line */
3078 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3085 if (ttyDisplay
->color
!= NO_COLOR
) {
3087 ttyDisplay
->color
= NO_COLOR
;
3097 register char ch
= (char) in_ch
;
3099 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3100 if (SYMHANDLING(H_IBM
) || iflags
.eight_bit_tty
) {
3101 /* IBM-compatible displays don't need other stuff */
3103 } else if (ch
& 0x80) {
3104 if (!GFlag
|| HE_resets_AS
) {
3108 (void) putchar((ch
^ 0x80)); /* Strip 8th bit */
3120 #endif /* ASCIIGRAPH && !NO_TERMS */
3137 tty_cliparound(x
, y
)
3140 extern boolean restoring
;
3141 int oldx
= clipx
, oldy
= clipy
;
3145 if (x
< clipx
+ 5) {
3146 clipx
= max(0, x
- 20);
3147 clipxmax
= clipx
+ CO
;
3148 } else if (x
> clipxmax
- 5) {
3149 clipxmax
= min(COLNO
, clipxmax
+ 20);
3150 clipx
= clipxmax
- CO
;
3152 if (y
< clipy
+ 2) {
3153 clipy
= max(0, y
- (clipymax
- clipy
) / 2);
3154 clipymax
= clipy
+ (LI
- 3);
3155 } else if (y
> clipymax
- 2) {
3156 clipymax
= min(ROWNO
, clipymax
+ (clipymax
- clipy
) / 2);
3157 clipy
= clipymax
- (LI
- 3);
3159 if (clipx
!= oldx
|| clipy
!= oldy
) {
3160 if (on_level(&u
.uz0
, &u
.uz
) && !restoring
)
3164 #endif /* CLIPPING */
3169 * Print the glyph to the output device. Don't flush the output device.
3171 * Since this is only called from show_glyph(), it is assumed that the
3172 * position and glyph are always correct (checked there)!
3176 tty_print_glyph(window
, x
, y
, glyph
, bkglyph
)
3183 boolean reverse_on
= FALSE
;
3189 if (x
<= clipx
|| y
< clipy
|| x
>= clipxmax
|| y
>= clipymax
)
3193 /* map glyph to character and color */
3194 (void) mapglyph(glyph
, &ch
, &color
, &special
, x
, y
);
3196 print_vt_code2(AVTC_SELECT_WINDOW
, window
);
3198 /* Move the cursor. */
3199 tty_curs(window
, x
, y
);
3201 print_vt_code3(AVTC_GLYPH_START
, glyph2tile
[glyph
], special
);
3204 if (ul_hack
&& ch
== '_') { /* non-destructive underscore */
3205 (void) putchar((char) ' ');
3211 if (color
!= ttyDisplay
->color
) {
3212 if (ttyDisplay
->color
!= NO_COLOR
)
3214 ttyDisplay
->color
= color
;
3215 if (color
!= NO_COLOR
)
3216 term_start_color(color
);
3218 #endif /* TEXTCOLOR */
3220 /* must be after color check; term_end_color may turn off inverse too */
3221 if (((special
& MG_PET
) && iflags
.hilite_pet
)
3222 || ((special
& MG_OBJPILE
) && iflags
.hilite_pile
)
3223 || ((special
& MG_DETECT
) && iflags
.use_inverse
)) {
3224 term_start_attr(ATR_INVERSE
);
3228 #if defined(USE_TILES) && defined(MSDOS)
3229 if (iflags
.grmode
&& iflags
.tile_view
)
3230 xputg(glyph
, ch
, special
);
3233 g_putch(ch
); /* print the character */
3236 term_end_attr(ATR_INVERSE
);
3238 /* turn off color as well, ATR_INVERSE may have done this already */
3239 if (ttyDisplay
->color
!= NO_COLOR
) {
3241 ttyDisplay
->color
= NO_COLOR
;
3246 print_vt_code1(AVTC_GLYPH_END
);
3248 wins
[window
]->curx
++; /* one character over */
3249 ttyDisplay
->curx
++; /* the real cursor moved too */
3257 ttyDisplay
->rawprint
++;
3258 print_vt_code2(AVTC_SELECT_WINDOW
, NHW_BASE
);
3259 #if defined(MICRO) || defined(WIN32CON)
3263 (void) fflush(stdout
);
3268 tty_raw_print_bold(str
)
3272 ttyDisplay
->rawprint
++;
3273 print_vt_code2(AVTC_SELECT_WINDOW
, NHW_BASE
);
3274 term_start_raw_bold();
3275 #if defined(MICRO) || defined(WIN32CON)
3278 (void) fputs(str
, stdout
);
3280 term_end_raw_bold();
3281 #if defined(MICRO) || defined(WIN32CON)
3285 (void) fflush(stdout
);
3294 /* kludge alert: Some Unix variants return funny values if getc()
3295 * is called, interrupted, and then called again. There
3296 * is non-reentrant code in the internal _filbuf() routine, called by
3299 static volatile int nesting
= 0;
3303 print_vt_code1(AVTC_INLINE_SYNC
);
3304 (void) fflush(stdout
);
3305 /* Note: if raw_print() and wait_synch() get called to report terminal
3306 * initialization problems, then wins[] and ttyDisplay might not be
3307 * available yet. Such problems will probably be fatal before we get
3308 * here, but validate those pointers just in case...
3310 if (WIN_MESSAGE
!= WIN_ERR
&& wins
[WIN_MESSAGE
])
3311 wins
[WIN_MESSAGE
]->flags
&= ~WIN_STOP
;
3313 i
= (++nesting
== 1) ? tgetch()
3314 : (read(fileno(stdin
), (genericptr_t
) &nestbuf
, 1)
3315 == 1) ? (int) nestbuf
: EOF
;
3321 i
= '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
3323 i
= '\033'; /* same for EOF */
3324 if (ttyDisplay
&& ttyDisplay
->toplin
== 1)
3325 ttyDisplay
->toplin
= 2;
3326 #ifdef TTY_TILES_ESCCODES
3328 /* hack to force output of the window select code */
3329 int tmp
= vt_tile_current_window
;
3330 vt_tile_current_window
++;
3331 print_vt_code2(AVTC_SELECT_WINDOW
, tmp
);
3333 #endif /* TTY_TILES_ESCCODES */
3338 * return a key, or 0, in which case a mouse button was pressed
3339 * mouse events should be returned as character postitions in the map window.
3340 * Since normal tty's don't have mice, just return a key.
3344 tty_nh_poskey(x
, y
, mod
)
3347 #if defined(WIN32CON)
3349 (void) fflush(stdout
);
3350 /* Note: if raw_print() and wait_synch() get called to report terminal
3351 * initialization problems, then wins[] and ttyDisplay might not be
3352 * available yet. Such problems will probably be fatal before we get
3353 * here, but validate those pointers just in case...
3355 if (WIN_MESSAGE
!= WIN_ERR
&& wins
[WIN_MESSAGE
])
3356 wins
[WIN_MESSAGE
]->flags
&= ~WIN_STOP
;
3357 i
= ntposkey(x
, y
, mod
);
3358 if (!i
&& mod
&& (*mod
== 0 || *mod
== EOF
))
3359 i
= '\033'; /* map NUL or EOF to ESC, nethack doesn't expect either */
3360 if (ttyDisplay
&& ttyDisplay
->toplin
== 1)
3361 ttyDisplay
->toplin
= 2;
3363 #else /* !WIN32CON */
3368 return tty_nhgetch();
3369 #endif /* ?WIN32CON */
3378 #if defined(WIN32CON)
3379 if (!strncmpi(windowprocs
.name
, "tty", 3))
3387 tty_update_positionbar(posbar
)
3391 video_update_positionbar(posbar
);
3396 #ifdef STATUS_VIA_WINDOWPORT
3398 * The following data structures come from the genl_ routines in
3399 * src/windows.c and as such are considered to be on the window-port
3400 * "side" of things, rather than the NetHack-core "side" of things.
3403 extern const char *status_fieldnm
[MAXBLSTATS
];
3404 extern const char *status_fieldfmt
[MAXBLSTATS
];
3405 extern char *status_vals
[MAXBLSTATS
];
3406 extern boolean status_activefields
[MAXBLSTATS
];
3407 extern winid WIN_STATUS
;
3409 #ifdef STATUS_HILITES
3410 typedef struct hilite_data_struct
{
3417 static hilite_data_t tty_status_hilites
[MAXBLSTATS
];
3418 static int tty_status_colors
[MAXBLSTATS
];
3420 struct color_option
{
3425 static void FDECL(start_color_option
, (struct color_option
));
3426 static void FDECL(end_color_option
, (struct color_option
));
3427 static void FDECL(apply_color_option
, (struct color_option
, const char *));
3428 static void FDECL(add_colored_text
, (const char *, char *));
3436 /* let genl_status_init do most of the initialization */
3439 for (i
= 0; i
< MAXBLSTATS
; ++i
) {
3440 #ifdef STATUS_HILITES
3441 tty_status_colors
[i
] = NO_COLOR
; /* no color */
3442 tty_status_hilites
[i
].thresholdtype
= 0;
3443 tty_status_hilites
[i
].behavior
= BL_TH_NONE
;
3444 tty_status_hilites
[i
].under
= BL_HILITE_NONE
;
3445 tty_status_hilites
[i
].over
= BL_HILITE_NONE
;
3446 #endif /* STATUS_HILITES */
3452 * -- update the value of a status field.
3453 * -- the fldindex identifies which field is changing and
3454 * is an integer index value from botl.h
3455 * -- fldindex could be any one of the following from botl.h:
3456 * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3457 * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
3458 * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
3459 * BL_LEVELDESC, BL_EXP, BL_CONDITION
3460 * -- fldindex could also be BL_FLUSH (-1), which is not really
3461 * a field index, but is a special trigger to tell the
3462 * windowport that it should redisplay all its status fields,
3463 * even if no changes have been presented to it.
3464 * -- ptr is usually a "char *", unless fldindex is BL_CONDITION.
3465 * If fldindex is BL_CONDITION, then ptr is a long value with
3466 * any or none of the following bits set (from botl.h):
3467 * BL_MASK_STONE 0x00000001L
3468 * BL_MASK_SLIME 0x00000002L
3469 * BL_MASK_STRNGL 0x00000004L
3470 * BL_MASK_FOODPOIS 0x00000008L
3471 * BL_MASK_TERMILL 0x00000010L
3472 * BL_MASK_BLIND 0x00000020L
3473 * BL_MASK_DEAF 0x00000040L
3474 * BL_MASK_STUN 0x00000080L
3475 * BL_MASK_CONF 0x00000100L
3476 * BL_MASK_HALLU 0x00000200L
3477 * BL_MASK_LEV 0x00000400L
3478 * BL_MASK_FLY 0x00000800L
3479 * BL_MASK_RIDE 0x00001000L
3480 * -- The value passed for BL_GOLD includes an encoded leading
3481 * symbol for GOLD "\GXXXXNNNN:nnn". If the window port needs to use
3482 * the textual gold amount without the leading "$:" the port will
3483 * have to skip past ':' in the passed "ptr" for the BL_GOLD case.
3486 tty_status_update(fldidx
, ptr
, chg
, percent
)
3487 int fldidx
, chg
, percent
;
3490 long cond
, *condptr
= (long *) ptr
;
3492 char *text
= (char *) ptr
;
3493 /* Mapping BL attributes to tty attributes
3494 * BL_HILITE_NONE -1 + 3 = 2 (statusattr[2])
3495 * BL_HILITE_INVERSE -2 + 3 = 1 (statusattr[1])
3496 * BL_HILITE_BOLD -3 + 3 = 0 (statusattr[0])
3499 static boolean beenhere
= FALSE
;
3500 enum statusfields fieldorder
[2][15] = {
3501 { BL_TITLE
, BL_STR
, BL_DX
, BL_CO
, BL_IN
, BL_WI
, BL_CH
, BL_ALIGN
,
3502 BL_SCORE
, BL_FLUSH
, BL_FLUSH
, BL_FLUSH
, BL_FLUSH
, BL_FLUSH
,
3504 { BL_LEVELDESC
, BL_GOLD
, BL_HP
, BL_HPMAX
, BL_ENE
, BL_ENEMAX
,
3505 BL_AC
, BL_XP
, BL_EXP
, BL_HD
, BL_TIME
, BL_HUNGER
,
3506 BL_CAP
, BL_CONDITION
, BL_FLUSH
}
3508 #ifdef STATUS_HILITES
3509 static int statusattr
[] = { ATR_BOLD
, ATR_INVERSE
, ATR_NONE
};
3516 if (fldidx
!= BL_FLUSH
) {
3517 if (!status_activefields
[fldidx
])
3522 *status_vals
[fldidx
] = '\0';
3523 if (cond
& BL_MASK_STONE
)
3524 Strcat(status_vals
[fldidx
], " Stone");
3525 if (cond
& BL_MASK_SLIME
)
3526 Strcat(status_vals
[fldidx
], " Slime");
3527 if (cond
& BL_MASK_STRNGL
)
3528 Strcat(status_vals
[fldidx
], " Strngl");
3529 if (cond
& BL_MASK_FOODPOIS
)
3530 Strcat(status_vals
[fldidx
], " FoodPois");
3531 if (cond
& BL_MASK_TERMILL
)
3532 Strcat(status_vals
[fldidx
], " TermIll");
3533 if (cond
& BL_MASK_BLIND
)
3534 Strcat(status_vals
[fldidx
], " Blind");
3535 if (cond
& BL_MASK_DEAF
)
3536 Strcat(status_vals
[fldidx
], " Deaf");
3537 if (cond
& BL_MASK_STUN
)
3538 Strcat(status_vals
[fldidx
], " Stun");
3539 if (cond
& BL_MASK_CONF
)
3540 Strcat(status_vals
[fldidx
], " Conf");
3541 if (cond
& BL_MASK_HALLU
)
3542 Strcat(status_vals
[fldidx
], " Hallu");
3543 if (cond
& BL_MASK_LEV
)
3544 Strcat(status_vals
[fldidx
], " Lev");
3545 if (cond
& BL_MASK_FLY
)
3546 Strcat(status_vals
[fldidx
], " Fly");
3547 if (cond
& BL_MASK_RIDE
)
3548 Strcat(status_vals
[fldidx
], " Ride");
3553 Sprintf(status_vals
[fldidx
],
3554 status_fieldfmt
[fldidx
] ? status_fieldfmt
[fldidx
] : "%s",
3559 #ifdef STATUS_HILITES
3560 switch (tty_status_hilites
[fldidx
].behavior
) {
3562 tty_status_colors
[fldidx
] = NO_COLOR
;
3566 tty_status_colors
[fldidx
] = tty_status_hilites
[fldidx
].over
;
3568 tty_status_colors
[fldidx
] = tty_status_hilites
[fldidx
].under
;
3570 tty_status_colors
[fldidx
] = NO_COLOR
;
3572 case BL_TH_VAL_PERCENTAGE
: {
3575 if (tty_status_hilites
[fldidx
].thresholdtype
!= ANY_INT
) {
3577 "tty_status_update: unsupported percentage threshold type %d",
3578 tty_status_hilites
[fldidx
].thresholdtype
);
3580 pct_th
= tty_status_hilites
[fldidx
].threshold
.a_int
;
3581 tty_status_colors
[fldidx
] = (percent
>= pct_th
)
3582 ? tty_status_hilites
[fldidx
].over
3583 : tty_status_hilites
[fldidx
].under
;
3587 case BL_TH_VAL_ABSOLUTE
: {
3589 int o
= tty_status_hilites
[fldidx
].over
;
3590 int u
= tty_status_hilites
[fldidx
].under
;
3591 anything
*t
= &tty_status_hilites
[fldidx
].threshold
;
3593 switch (tty_status_hilites
[fldidx
].thresholdtype
) {
3595 c
= (value
>= t
->a_long
) ? o
: u
;
3598 c
= (value
>= t
->a_int
) ? o
: u
;
3601 c
= ((unsigned long) value
>= t
->a_uint
) ? o
: u
;
3604 c
= ((unsigned long) value
>= t
->a_ulong
) ? o
: u
;
3607 c
= (value
& t
->a_ulong
) ? o
: u
;
3611 "tty_status_update: unsupported absolute threshold type %d\n",
3612 tty_status_hilites
[fldidx
].thresholdtype
);
3615 tty_status_colors
[fldidx
] = c
;
3619 #endif /* STATUS_HILITES */
3622 /* For now, this version copied from the genl_ version currently
3623 * updates everything on the display, everytime
3626 if (!beenhere
|| !iflags
.use_status_hilites
) {
3627 char newbot1
[MAXCO
], newbot2
[MAXCO
];
3630 for (i
= 0; fieldorder
[0][i
] >= 0; ++i
) {
3631 int idx1
= fieldorder
[0][i
];
3633 if (status_activefields
[idx1
])
3634 Strcat(newbot1
, status_vals
[idx1
]);
3637 for (i
= 0; fieldorder
[1][i
] >= 0; ++i
) {
3638 int idx2
= fieldorder
[1][i
];
3640 if (status_activefields
[idx2
])
3641 Strcat(newbot2
, status_vals
[idx2
]);
3644 curs(WIN_STATUS
, 1, 0);
3645 putstr(WIN_STATUS
, 0, newbot1
);
3646 curs(WIN_STATUS
, 1, 1);
3647 putmixed(WIN_STATUS
, 0, newbot2
); /* putmixed() due to GOLD glyph */
3652 curs(WIN_STATUS
, 1, 0);
3653 for (i
= 0; fieldorder
[0][i
] != BL_FLUSH
; ++i
) {
3654 int fldidx1
= fieldorder
[0][i
];
3656 if (status_activefields
[fldidx1
]) {
3657 #ifdef STATUS_HILITES
3658 if (tty_status_colors
[fldidx1
] < 0
3659 && tty_status_colors
[fldidx1
] >= -3) {
3660 /* attribute, not a color */
3661 attridx
= tty_status_colors
[fldidx1
] + 3;
3662 term_start_attr(statusattr
[attridx
]);
3663 putstr(WIN_STATUS
, 0, status_vals
[fldidx1
]);
3664 term_end_attr(statusattr
[attridx
]);
3667 if (tty_status_colors
[fldidx1
] != CLR_MAX
) {
3668 if (tty_status_colors
[fldidx1
] != NO_COLOR
)
3669 term_start_color(tty_status_colors
[fldidx1
]);
3670 putstr(WIN_STATUS
, 0, status_vals
[fldidx1
]);
3671 if (tty_status_colors
[fldidx1
] != NO_COLOR
)
3675 #endif /* STATUS_HILITES */
3676 putstr(WIN_STATUS
, 0, status_vals
[fldidx1
]);
3679 curs(WIN_STATUS
, 1, 1);
3680 for (i
= 0; fieldorder
[1][i
] != BL_FLUSH
; ++i
) {
3681 int fldidx2
= fieldorder
[1][i
];
3683 if (status_activefields
[fldidx2
]) {
3684 #ifdef STATUS_HILITES
3685 if (tty_status_colors
[fldidx2
] < 0
3686 && tty_status_colors
[fldidx2
] >= -3) {
3687 /* attribute, not a color */
3688 attridx
= tty_status_colors
[fldidx2
] + 3;
3689 term_start_attr(statusattr
[attridx
]);
3690 putstr(WIN_STATUS
, 0, status_vals
[fldidx2
]);
3691 term_end_attr(statusattr
[attridx
]);
3694 if (tty_status_colors
[fldidx2
] != CLR_MAX
) {
3695 if (tty_status_colors
[fldidx2
] != NO_COLOR
)
3696 term_start_color(tty_status_colors
[fldidx2
]);
3697 if (fldidx2
== BL_GOLD
) {
3698 /* putmixed() due to GOLD glyph */
3699 putmixed(WIN_STATUS
, 0, status_vals
[fldidx2
]);
3701 putstr(WIN_STATUS
, 0, status_vals
[fldidx2
]);
3703 if (tty_status_colors
[fldidx2
] != NO_COLOR
)
3707 #endif /* STATUS_HILITES */
3708 putstr(WIN_STATUS
, 0, status_vals
[fldidx2
]);
3714 #ifdef STATUS_HILITES
3716 * status_threshold(int fldidx, int threshholdtype, anything threshold,
3717 * int behavior, int under, int over)
3719 * -- called when a hiliting preference is added, changed, or
3721 * -- the fldindex identifies which field is having its hiliting
3722 * preference set. It is an integer index value from botl.h
3723 * -- fldindex could be any one of the following from botl.h:
3724 * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3725 * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
3726 * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
3727 * BL_LEVELDESC, BL_EXP, BL_CONDITION
3728 * -- datatype is P_INT, P_UINT, P_LONG, or P_MASK.
3729 * -- threshold is an "anything" union which can contain the
3731 * -- behavior is used to define how threshold is used and can
3732 * be BL_TH_NONE, BL_TH_VAL_PERCENTAGE, BL_TH_VAL_ABSOLUTE,
3733 * or BL_TH_UPDOWN. BL_TH_NONE means don't do anything above
3734 * or below the threshold. BL_TH_VAL_PERCENTAGE treats the
3735 * threshold value as a precentage of the maximum possible
3736 * value. BL_TH_VAL_ABSOLUTE means that the threshold is an
3737 * actual value. BL_TH_UPDOWN means that threshold is not
3738 * used, and the two below/above hilite values indicate how
3739 * to display something going down (under) or rising (over).
3740 * -- under is the hilite attribute used if value is below the
3741 * threshold. The attribute can be BL_HILITE_NONE,
3742 * BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one
3743 * of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN,
3744 * CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY,
3745 * CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE,
3746 * CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15).
3747 * -- over is the hilite attribute used if value is at or above
3748 * the threshold. The attribute can be BL_HILITE_NONE,
3749 * BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one
3750 * of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN,
3751 * CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY,
3752 * CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE,
3753 * CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15).
3756 tty_status_threshold(fldidx
, thresholdtype
, threshold
, behavior
, under
, over
)
3757 int fldidx
, thresholdtype
;
3758 int behavior
, under
, over
;
3761 tty_status_hilites
[fldidx
].thresholdtype
= thresholdtype
;
3762 tty_status_hilites
[fldidx
].threshold
= threshold
;
3763 tty_status_hilites
[fldidx
].behavior
= behavior
;
3764 tty_status_hilites
[fldidx
].under
= under
;
3765 tty_status_hilites
[fldidx
].over
= over
;
3769 #endif /* STATUS_HILITES */
3770 #endif /*STATUS_VIA_WINDOWPORT*/
3772 #endif /* TTY_GRAPHICS */