1 /* SCCS Id: @(#)wintty.c 3.4 2002/09/27 */
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.
13 #ifdef SHORT_FILENAMES
16 #include "patchlevel.h"
22 # define MICRO /* The Mac is a MICRO only for this file, not in general! */
24 extern void msmsg(const char *,...);
35 #ifdef CLIPPING /* might want SIGWINCH */
36 # if defined(BSD) || defined(ULTRIX) || defined(AIX_31) || defined(_BULL_SOURCE)
41 extern char mapped_menu_cmds[]; /* from options.c */
43 /* Interface definition, for windows.c */
44 struct window_procs tty_procs = {
47 WC_TILED_MAP|WC_ASCII_MAP|
52 WC_COLOR|WC_HILITE_PET|WC_INVERSE|WC_EIGHT_BIT_IN,
59 tty_suspend_nhwindows,
80 tty_update_positionbar,
94 #ifdef CHANGE_COLOR /* the Mac uses a palette device */
97 tty_change_background,
100 tty_get_color_string,
103 /* other defs that really should go away (they're tty specific) */
107 #if defined(WIN32CON)
108 nttty_preference_update,
110 genl_preference_update,
114 static int maxwin = 0; /* number of windows in use */
116 struct WinDesc *wins[MAXWIN];
117 struct DisplayDesc *ttyDisplay; /* the tty display descriptor */
119 extern void FDECL(cmov, (int,int)); /* from termcap.c */
120 extern void FDECL(nocmov, (int,int)); /* from termcap.c */
121 #if defined(UNIX) || defined(VMS)
122 static char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */
125 static char winpanicstr[] = "Bad window id %d";
126 char defmorestr[] = "--More--";
129 # if defined(USE_TILES) && defined(MSDOS)
130 boolean clipping = FALSE; /* clipping on? */
131 int clipx = 0, clipxmax = 0;
133 static boolean clipping = FALSE; /* clipping on? */
134 static int clipx = 0, clipxmax = 0;
136 static int clipy = 0, clipymax = 0;
137 #endif /* CLIPPING */
139 #if defined(USE_TILES) && defined(MSDOS)
140 extern void FDECL(adjust_cursor_flags, (struct WinDesc *));
143 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
144 boolean GFlag = FALSE;
145 boolean HE_resets_AS; /* see termcap.c */
148 #if defined(MICRO) || defined(WIN32CON)
149 static const char to_continue[] = "to continue";
150 #define getret() getreturn(to_continue)
152 STATIC_DCL void NDECL(getret);
154 STATIC_DCL void FDECL(erase_menu_or_text, (winid, struct WinDesc *, BOOLEAN_P));
155 STATIC_DCL void FDECL(free_window_info, (struct WinDesc *, BOOLEAN_P));
156 STATIC_DCL void FDECL(dmore,(struct WinDesc *, const char *));
157 STATIC_DCL void FDECL(set_item_state, (winid, int, tty_menu_item *));
158 STATIC_DCL void FDECL(set_all_on_page, (winid,tty_menu_item *,tty_menu_item *));
159 STATIC_DCL void FDECL(unset_all_on_page, (winid,tty_menu_item *,tty_menu_item *));
160 STATIC_DCL void FDECL(invert_all_on_page, (winid,tty_menu_item *,tty_menu_item *, CHAR_P));
161 STATIC_DCL void FDECL(invert_all, (winid,tty_menu_item *,tty_menu_item *, CHAR_P));
162 STATIC_DCL void FDECL(process_menu_window, (winid,struct WinDesc *));
163 STATIC_DCL void FDECL(process_text_window, (winid,struct WinDesc *));
164 STATIC_DCL tty_menu_item *FDECL(reverse, (tty_menu_item *));
165 STATIC_DCL const char * FDECL(compress_str, (const char *));
166 STATIC_DCL void FDECL(tty_putsym, (winid, int, int, CHAR_P));
167 static char *FDECL(copy_of, (const char *));
168 STATIC_DCL void FDECL(bail, (const char *)); /* __attribute__((noreturn)) */
171 * A string containing all the default commands -- to add to a list
172 * of acceptable inputs.
174 static const char default_menu_cmds[] = {
185 0 /* null terminator */
189 /* clean up and quit */
195 tty_exit_nhwindows(mesg);
196 terminate(EXIT_SUCCESS);
200 #if defined(SIGWINCH) && defined(CLIPPING)
204 int oldLI = LI, oldCO = CO, i;
205 register struct WinDesc *cw;
208 if((oldLI != LI || oldCO != CO) && ttyDisplay) {
209 ttyDisplay->rows = LI;
210 ttyDisplay->cols = CO;
212 cw = wins[BASE_WINDOW];
213 cw->rows = ttyDisplay->rows;
214 cw->cols = ttyDisplay->cols;
216 if(iflags.window_inited) {
217 cw = wins[WIN_MESSAGE];
218 cw->curx = cw->cury = 0;
220 tty_destroy_nhwindow(WIN_STATUS);
221 WIN_STATUS = tty_create_nhwindow(NHW_STATUS);
225 if(CO < COLNO || LI < ROWNO+3) {
227 tty_cliparound(u.ux, u.uy);
233 i = ttyDisplay->toplin;
234 ttyDisplay->toplin = 0;
237 ttyDisplay->toplin = i;
242 for(i=WIN_INVEN; i < MAXWIN; i++)
243 if(wins[i] && wins[i]->active) {
245 addtopl("Press Return to continue: ");
248 (void) fflush(stdout);
249 if(i < 2) flush_screen(1);
258 tty_init_nhwindows(argcp,argv)
265 * Remember tty modes, to be restored on exit.
267 * gettty() must be called before tty_startup()
268 * due to ordering of LI/CO settings
269 * tty_startup() must be called before initoptions()
270 * due to ordering of graphics settings
272 #if defined(UNIX) || defined(VMS)
277 /* to port dependant tty setup */
278 tty_startup(&wid, &hgt);
279 setftty(); /* calls start_screen */
281 /* set up tty descriptor */
282 ttyDisplay = (struct DisplayDesc*) alloc(sizeof(struct DisplayDesc));
283 ttyDisplay->toplin = 0;
284 ttyDisplay->rows = hgt;
285 ttyDisplay->cols = wid;
286 ttyDisplay->curx = ttyDisplay->cury = 0;
287 ttyDisplay->inmore = ttyDisplay->inread = ttyDisplay->intr = 0;
288 ttyDisplay->dismiss_more = 0;
290 ttyDisplay->color = NO_COLOR;
292 ttyDisplay->attrs = 0;
294 /* set up the default windows */
295 BASE_WINDOW = tty_create_nhwindow(NHW_BASE);
296 wins[BASE_WINDOW]->active = 1;
298 ttyDisplay->lastwin = WIN_ERR;
300 #if defined(SIGWINCH) && defined(CLIPPING)
301 (void) signal(SIGWINCH, winch);
304 /* add one a space forward menu command alias */
305 add_menu_cmd_alias(' ', MENU_NEXT_PAGE);
307 tty_clear_nhwindow(BASE_WINDOW);
309 tty_putstr(BASE_WINDOW, 0, "");
310 tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_A);
311 tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_B);
312 tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_B2);
313 tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_C);
314 tty_putstr(BASE_WINDOW, 0, "");
315 tty_display_nhwindow(BASE_WINDOW, FALSE);
319 tty_player_selection()
322 char pick4u = 'n', thisch, lastch = 0;
323 char pbuf[QBUFSZ], plbuf[QBUFSZ];
326 menu_item *selected = 0;
328 /* prevent an unnecessary prompt */
331 /* Should we randomly pick for the player? */
332 if (!flags.randomall &&
333 (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE ||
334 flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) {
336 char *prompt = build_plselection_prompt(pbuf, QBUFSZ, flags.initrole,
337 flags.initrace, flags.initgend, flags.initalign);
339 tty_putstr(BASE_WINDOW, 0, "");
340 echoline = wins[BASE_WINDOW]->cury;
341 tty_putstr(BASE_WINDOW, 0, prompt);
343 pick4u = lowc(readchar());
344 if (index(quitchars, pick4u)) pick4u = 'y';
345 } while(!index(ynqchars, pick4u));
346 if ((int)strlen(prompt) + 1 < CO) {
347 /* Echo choice and move back down line */
348 tty_putsym(BASE_WINDOW, (int)strlen(prompt)+1, echoline, pick4u);
349 tty_putstr(BASE_WINDOW, 0, "");
351 /* Otherwise it's hard to tell where to echo, and things are
352 * wrapping a bit messily anyway, so (try to) make sure the next
353 * question shows up well and doesn't get wrapped at the
354 * bottom of the window.
356 tty_clear_nhwindow(BASE_WINDOW);
358 if (pick4u != 'y' && pick4u != 'n') {
360 if (selected) free((genericptr_t) selected);
367 (void) root_plselection_prompt(plbuf, QBUFSZ - 1,
368 flags.initrole, flags.initrace, flags.initgend, flags.initalign);
370 /* Select a role, if necessary */
371 /* we'll try to be compatible with pre-selected race/gender/alignment,
372 * but may not succeed */
373 if (flags.initrole < 0) {
374 char rolenamebuf[QBUFSZ];
375 /* Process the choice */
376 if (pick4u == 'y' || flags.initrole == ROLE_RANDOM || flags.randomall) {
377 /* Pick a random role */
378 flags.initrole = pick_role(flags.initrace, flags.initgend,
379 flags.initalign, PICK_RANDOM);
380 if (flags.initrole < 0) {
381 tty_putstr(BASE_WINDOW, 0, "Incompatible role!");
382 flags.initrole = randrole();
385 tty_clear_nhwindow(BASE_WINDOW);
386 tty_putstr(BASE_WINDOW, 0, "Choosing Character's Role");
387 /* Prompt for a role */
388 win = create_nhwindow(NHW_MENU);
390 any.a_void = 0; /* zero out all bits */
391 for (i = 0; roles[i].name.m; i++) {
392 if (ok_role(i, flags.initrace, flags.initgend,
394 any.a_int = i+1; /* must be non-zero */
395 thisch = lowc(roles[i].name.m[0]);
396 if (thisch == lastch) thisch = highc(thisch);
397 if (flags.initgend != ROLE_NONE && flags.initgend != ROLE_RANDOM) {
398 if (flags.initgend == 1 && roles[i].name.f)
399 Strcpy(rolenamebuf, roles[i].name.f);
401 Strcpy(rolenamebuf, roles[i].name.m);
403 if (roles[i].name.f) {
404 Strcpy(rolenamebuf, roles[i].name.m);
405 Strcat(rolenamebuf, "/");
406 Strcat(rolenamebuf, roles[i].name.f);
408 Strcpy(rolenamebuf, roles[i].name.m);
410 add_menu(win, NO_GLYPH, &any, thisch,
411 0, ATR_NONE, an(rolenamebuf), MENU_UNSELECTED);
415 any.a_int = pick_role(flags.initrace, flags.initgend,
416 flags.initalign, PICK_RANDOM)+1;
417 if (any.a_int == 0) /* must be non-zero */
418 any.a_int = randrole()+1;
419 add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
420 "Random", MENU_UNSELECTED);
421 any.a_int = i+1; /* must be non-zero */
422 add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
423 "Quit", MENU_UNSELECTED);
424 Sprintf(pbuf, "Pick a role for your %s", plbuf);
426 n = select_menu(win, PICK_ONE, &selected);
427 destroy_nhwindow(win);
429 /* Process the choice */
430 if (n != 1 || selected[0].item.a_int == any.a_int)
431 goto give_up; /* Selected quit */
433 flags.initrole = selected[0].item.a_int - 1;
434 free((genericptr_t) selected), selected = 0;
436 (void) root_plselection_prompt(plbuf, QBUFSZ - 1,
437 flags.initrole, flags.initrace, flags.initgend, flags.initalign);
440 /* Select a race, if necessary */
441 /* force compatibility with role, try for compatibility with
442 * pre-selected gender/alignment */
443 if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
444 /* pre-selected race not valid */
445 if (pick4u == 'y' || flags.initrace == ROLE_RANDOM || flags.randomall) {
446 flags.initrace = pick_race(flags.initrole, flags.initgend,
447 flags.initalign, PICK_RANDOM);
448 if (flags.initrace < 0) {
449 tty_putstr(BASE_WINDOW, 0, "Incompatible race!");
450 flags.initrace = randrace(flags.initrole);
452 } else { /* pick4u == 'n' */
453 /* Count the number of valid races */
454 n = 0; /* number valid */
455 k = 0; /* valid race */
456 for (i = 0; races[i].noun; i++) {
457 if (ok_race(flags.initrole, i, flags.initgend,
464 for (i = 0; races[i].noun; i++) {
465 if (validrace(flags.initrole, i)) {
472 /* Permit the user to pick, if there is more than one */
474 tty_clear_nhwindow(BASE_WINDOW);
475 tty_putstr(BASE_WINDOW, 0, "Choosing Race");
476 win = create_nhwindow(NHW_MENU);
478 any.a_void = 0; /* zero out all bits */
479 for (i = 0; races[i].noun; i++)
480 if (ok_race(flags.initrole, i, flags.initgend,
482 any.a_int = i+1; /* must be non-zero */
483 add_menu(win, NO_GLYPH, &any, races[i].noun[0],
484 0, ATR_NONE, races[i].noun, MENU_UNSELECTED);
486 any.a_int = pick_race(flags.initrole, flags.initgend,
487 flags.initalign, PICK_RANDOM)+1;
488 if (any.a_int == 0) /* must be non-zero */
489 any.a_int = randrace(flags.initrole)+1;
490 add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
491 "Random", MENU_UNSELECTED);
492 any.a_int = i+1; /* must be non-zero */
493 add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
494 "Quit", MENU_UNSELECTED);
495 Sprintf(pbuf, "Pick the race of your %s", plbuf);
497 n = select_menu(win, PICK_ONE, &selected);
498 destroy_nhwindow(win);
499 if (n != 1 || selected[0].item.a_int == any.a_int)
500 goto give_up; /* Selected quit */
502 k = selected[0].item.a_int - 1;
503 free((genericptr_t) selected), selected = 0;
507 (void) root_plselection_prompt(plbuf, QBUFSZ - 1,
508 flags.initrole, flags.initrace, flags.initgend, flags.initalign);
511 /* Select a gender, if necessary */
512 /* force compatibility with role/race, try for compatibility with
513 * pre-selected alignment */
514 if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace,
516 /* pre-selected gender not valid */
517 if (pick4u == 'y' || flags.initgend == ROLE_RANDOM || flags.randomall) {
518 flags.initgend = pick_gend(flags.initrole, flags.initrace,
519 flags.initalign, PICK_RANDOM);
520 if (flags.initgend < 0) {
521 tty_putstr(BASE_WINDOW, 0, "Incompatible gender!");
522 flags.initgend = randgend(flags.initrole, flags.initrace);
524 } else { /* pick4u == 'n' */
525 /* Count the number of valid genders */
526 n = 0; /* number valid */
527 k = 0; /* valid gender */
528 for (i = 0; i < ROLE_GENDERS; i++) {
529 if (ok_gend(flags.initrole, flags.initrace, i,
536 for (i = 0; i < ROLE_GENDERS; i++) {
537 if (validgend(flags.initrole, flags.initrace, i)) {
544 /* Permit the user to pick, if there is more than one */
546 tty_clear_nhwindow(BASE_WINDOW);
547 tty_putstr(BASE_WINDOW, 0, "Choosing Gender");
548 win = create_nhwindow(NHW_MENU);
550 any.a_void = 0; /* zero out all bits */
551 for (i = 0; i < ROLE_GENDERS; i++)
552 if (ok_gend(flags.initrole, flags.initrace, i,
555 add_menu(win, NO_GLYPH, &any, genders[i].adj[0],
556 0, ATR_NONE, genders[i].adj, MENU_UNSELECTED);
558 any.a_int = pick_gend(flags.initrole, flags.initrace,
559 flags.initalign, PICK_RANDOM)+1;
560 if (any.a_int == 0) /* must be non-zero */
561 any.a_int = randgend(flags.initrole, flags.initrace)+1;
562 add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
563 "Random", MENU_UNSELECTED);
564 any.a_int = i+1; /* must be non-zero */
565 add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
566 "Quit", MENU_UNSELECTED);
567 Sprintf(pbuf, "Pick the gender of your %s", plbuf);
569 n = select_menu(win, PICK_ONE, &selected);
570 destroy_nhwindow(win);
571 if (n != 1 || selected[0].item.a_int == any.a_int)
572 goto give_up; /* Selected quit */
574 k = selected[0].item.a_int - 1;
575 free((genericptr_t) selected), selected = 0;
579 (void) root_plselection_prompt(plbuf, QBUFSZ - 1,
580 flags.initrole, flags.initrace, flags.initgend, flags.initalign);
583 /* Select an alignment, if necessary */
584 /* force compatibility with role/race/gender */
585 if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace,
587 /* pre-selected alignment not valid */
588 if (pick4u == 'y' || flags.initalign == ROLE_RANDOM || flags.randomall) {
589 flags.initalign = pick_align(flags.initrole, flags.initrace,
590 flags.initgend, PICK_RANDOM);
591 if (flags.initalign < 0) {
592 tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!");
593 flags.initalign = randalign(flags.initrole, flags.initrace);
595 } else { /* pick4u == 'n' */
596 /* Count the number of valid alignments */
597 n = 0; /* number valid */
598 k = 0; /* valid alignment */
599 for (i = 0; i < ROLE_ALIGNS; i++) {
600 if (ok_align(flags.initrole, flags.initrace, flags.initgend,
607 for (i = 0; i < ROLE_ALIGNS; i++) {
608 if (validalign(flags.initrole, flags.initrace, i)) {
615 /* Permit the user to pick, if there is more than one */
617 tty_clear_nhwindow(BASE_WINDOW);
618 tty_putstr(BASE_WINDOW, 0, "Choosing Alignment");
619 win = create_nhwindow(NHW_MENU);
621 any.a_void = 0; /* zero out all bits */
622 for (i = 0; i < ROLE_ALIGNS; i++)
623 if (ok_align(flags.initrole, flags.initrace,
624 flags.initgend, i)) {
626 add_menu(win, NO_GLYPH, &any, aligns[i].adj[0],
627 0, ATR_NONE, aligns[i].adj, MENU_UNSELECTED);
629 any.a_int = pick_align(flags.initrole, flags.initrace,
630 flags.initgend, PICK_RANDOM)+1;
631 if (any.a_int == 0) /* must be non-zero */
632 any.a_int = randalign(flags.initrole, flags.initrace)+1;
633 add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
634 "Random", MENU_UNSELECTED);
635 any.a_int = i+1; /* must be non-zero */
636 add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
637 "Quit", MENU_UNSELECTED);
638 Sprintf(pbuf, "Pick the alignment of your %s", plbuf);
640 n = select_menu(win, PICK_ONE, &selected);
641 destroy_nhwindow(win);
642 if (n != 1 || selected[0].item.a_int == any.a_int)
643 goto give_up; /* Selected quit */
645 k = selected[0].item.a_int - 1;
646 free((genericptr_t) selected), selected = 0;
652 tty_display_nhwindow(BASE_WINDOW, FALSE);
656 * plname is filled either by an option (-u Player or -uPlayer) or
657 * explicitly (by being the wizard) or by askname.
658 * It may still contain a suffix denoting the role, etc.
659 * Always called after init_nhwindows() and before display_gamewindows().
664 static char who_are_you[] = "Who are you? ";
665 register int c, ct, tryct = 0;
667 tty_putstr(BASE_WINDOW, 0, "");
670 if (tryct > 10) bail("Giving up after 10 tries.\n");
671 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury - 1);
672 tty_putstr(BASE_WINDOW, 0, "Enter a name for your character...");
673 /* erase previous prompt (in case of ESC after partial response) */
674 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury), cl_end();
676 tty_putstr(BASE_WINDOW, 0, who_are_you);
677 tty_curs(BASE_WINDOW, (int)(sizeof who_are_you),
678 wins[BASE_WINDOW]->cury - 1);
680 while((c = tty_nhgetch()) != '\n') {
681 if(c == EOF) error("End of input\n");
682 if (c == '\033') { ct = 0; break; } /* continue outer loop */
683 #if defined(WIN32CON)
684 if (c == '\003') bail("^C abort.\n");
686 /* some people get confused when their erase char is not ^H */
687 if (c == '\b' || c == '\177') {
693 #if defined(MICRO) || defined(WIN32CON)
694 # if defined(WIN32CON) || defined(MSDOS)
695 backsp(); /* \b is visible on NT */
702 (void) putchar('\b');
704 (void) putchar('\b');
709 #if defined(UNIX) || defined(VMS)
710 if(c != '-' && c != '@')
711 if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_';
713 if (ct < (int)(sizeof plname) - 1) {
733 /* move to next line to simulate echo of user's <return> */
734 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury + 1);
743 #if !defined(MICRO) && !defined(WIN32CON)
751 xputs(iflags.cbreak ? "space" : "return");
752 xputs(" to continue: ");
760 tty_suspend_nhwindows(str)
763 settty(str); /* calls end_screen, perhaps raw_print */
764 if (!str) tty_raw_print(""); /* calls fflush(stdout) */
768 tty_resume_nhwindows()
771 setftty(); /* calls start_screen */
776 tty_exit_nhwindows(str)
781 tty_suspend_nhwindows(str);
782 /* Just forget any windows existed, since we're about to exit anyway.
783 * Disable windows to avoid calls to window routines.
785 for(i=0; i<MAXWIN; i++)
786 if (wins[i] && (i != BASE_WINDOW)) {
787 #ifdef FREE_ALL_MEMORY
788 free_window_info(wins[i], TRUE);
789 free((genericptr_t) wins[i]);
793 #ifndef NO_TERMS /*(until this gets added to the window interface)*/
794 tty_shutdown(); /* cleanup termcap/terminfo/whatever */
796 iflags.window_inited = 0;
800 tty_create_nhwindow(type)
803 struct WinDesc* newwin;
810 newwin = (struct WinDesc*) alloc(sizeof(struct WinDesc));
813 newwin->active = FALSE;
814 newwin->curx = newwin->cury = 0;
816 newwin->mlist = (tty_menu_item *) 0;
817 newwin->plist = (tty_menu_item **) 0;
818 newwin->npages = newwin->plist_size = newwin->nitems = newwin->how = 0;
821 /* base window, used for absolute movement on the screen */
822 newwin->offx = newwin->offy = 0;
823 newwin->rows = ttyDisplay->rows;
824 newwin->cols = ttyDisplay->cols;
825 newwin->maxrow = newwin->maxcol = 0;
828 /* message window, 1 line long, very wide, top of screen */
829 newwin->offx = newwin->offy = 0;
831 if(iflags.msg_history < 20) iflags.msg_history = 20;
832 else if(iflags.msg_history > 60) iflags.msg_history = 60;
833 newwin->maxrow = newwin->rows = iflags.msg_history;
834 newwin->maxcol = newwin->cols = 0;
837 /* status window, 2 lines long, full width, bottom of screen */
839 #if defined(USE_TILES) && defined(MSDOS)
841 newwin->offy = ttyDisplay->rows-2;
844 newwin->offy = min((int)ttyDisplay->rows-2, ROWNO+1);
845 newwin->rows = newwin->maxrow = 2;
846 newwin->cols = newwin->maxcol = min(ttyDisplay->cols, COLNO);
849 /* map window, ROWNO lines long, full width, below message window */
852 newwin->rows = ROWNO;
853 newwin->cols = COLNO;
854 newwin->maxrow = 0; /* no buffering done -- let gbuf do it */
859 /* inventory/menu window, variable length, full width, top of screen */
860 /* help window, the same, different semantics for display, etc */
861 newwin->offx = newwin->offy = 0;
863 newwin->cols = ttyDisplay->cols;
864 newwin->maxrow = newwin->maxcol = 0;
867 panic("Tried to create window type %d\n", (int) type);
871 for(newid = 0; newid<MAXWIN; newid++) {
872 if(wins[newid] == 0) {
873 wins[newid] = newwin;
877 if(newid == MAXWIN) {
878 panic("No window slots!");
884 (char **) alloc(sizeof(char *) * (unsigned)newwin->maxrow);
886 (short *) alloc(sizeof(short) * (unsigned)newwin->maxrow);
888 for (i = 0; i < newwin->maxrow; i++) {
889 newwin->data[i] = (char *) alloc((unsigned)newwin->maxcol);
890 newwin->datlen[i] = newwin->maxcol;
893 for (i = 0; i < newwin->maxrow; i++) {
894 newwin->data[i] = (char *) 0;
895 newwin->datlen[i] = 0;
898 if(newwin->type == NHW_MESSAGE)
901 newwin->data = (char **)0;
902 newwin->datlen = (short *)0;
909 erase_menu_or_text(window, cw, clear)
916 tty_curs(window, 1, 0);
923 docorner((int)cw->offx, cw->maxrow+1);
927 free_window_info(cw, free_data)
934 if (cw == wins[WIN_MESSAGE] && cw->rows > cw->maxrow)
935 cw->maxrow = cw->rows; /* topl data */
936 for(i=0; i<cw->maxrow; i++)
938 free((genericptr_t)cw->data[i]);
939 cw->data[i] = (char *)0;
940 if (cw->datlen) cw->datlen[i] = 0;
943 free((genericptr_t)cw->data);
944 cw->data = (char **)0;
945 if (cw->datlen) free((genericptr_t)cw->datlen);
946 cw->datlen = (short *)0;
950 cw->maxrow = cw->maxcol = 0;
953 while ((temp = cw->mlist) != 0) {
954 cw->mlist = cw->mlist->next;
955 if (temp->str) free((genericptr_t)temp->str);
956 free((genericptr_t)temp);
960 free((genericptr_t)cw->plist);
963 cw->plist_size = cw->npages = cw->nitems = cw->how = 0;
965 free((genericptr_t)cw->morestr);
971 tty_clear_nhwindow(window)
974 register struct WinDesc *cw = 0;
976 if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
977 panic(winpanicstr, window);
978 ttyDisplay->lastwin = window;
982 if(ttyDisplay->toplin) {
986 docorner(1, cw->cury+1);
987 ttyDisplay->toplin = 0;
991 tty_curs(window, 1, 0);
993 tty_curs(window, 1, 1);
997 /* cheap -- clear the whole thing and tell nethack to redraw botl */
1006 erase_menu_or_text(window, cw, TRUE);
1007 free_window_info(cw, FALSE);
1010 cw->curx = cw->cury = 0;
1015 register struct WinDesc *cw;
1016 const char *s; /* valid responses */
1018 const char *prompt = cw->morestr ? cw->morestr : defmorestr;
1019 int offset = (cw->type == NHW_TEXT) ? 1 : 2;
1021 tty_curs(BASE_WINDOW,
1022 (int)ttyDisplay->curx + offset, (int)ttyDisplay->cury);
1026 ttyDisplay->curx += strlen(prompt);
1034 set_item_state(window, lineno, item)
1037 tty_menu_item *item;
1039 char ch = item->selected ? (item->count == -1L ? '+' : '#') : '-';
1040 tty_curs(window, 4, lineno);
1041 term_start_attr(item->attr);
1044 term_end_attr(item->attr);
1048 set_all_on_page(window, page_start, page_end)
1050 tty_menu_item *page_start, *page_end;
1052 tty_menu_item *curr;
1055 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1056 if (curr->identifier.a_void && !curr->selected) {
1057 curr->selected = TRUE;
1058 set_item_state(window, n, curr);
1063 unset_all_on_page(window, page_start, page_end)
1065 tty_menu_item *page_start, *page_end;
1067 tty_menu_item *curr;
1070 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1071 if (curr->identifier.a_void && curr->selected) {
1072 curr->selected = FALSE;
1074 set_item_state(window, n, curr);
1079 invert_all_on_page(window, page_start, page_end, acc)
1081 tty_menu_item *page_start, *page_end;
1082 char acc; /* group accelerator, 0 => all */
1084 tty_menu_item *curr;
1087 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1088 if (curr->identifier.a_void && (acc == 0 || curr->gselector == acc)) {
1089 if (curr->selected) {
1090 curr->selected = FALSE;
1093 curr->selected = TRUE;
1094 set_item_state(window, n, curr);
1099 * Invert all entries that match the give group accelerator (or all if
1103 invert_all(window, page_start, page_end, acc)
1105 tty_menu_item *page_start, *page_end;
1106 char acc; /* group accelerator, 0 => all */
1108 tty_menu_item *curr;
1109 boolean on_curr_page;
1110 struct WinDesc *cw = wins[window];
1112 invert_all_on_page(window, page_start, page_end, acc);
1114 /* invert the rest */
1115 for (on_curr_page = FALSE, curr = cw->mlist; curr; curr = curr->next) {
1116 if (curr == page_start)
1117 on_curr_page = TRUE;
1118 else if (curr == page_end)
1119 on_curr_page = FALSE;
1121 if (!on_curr_page && curr->identifier.a_void
1122 && (acc == 0 || curr->gselector == acc)) {
1123 if (curr->selected) {
1124 curr->selected = FALSE;
1127 curr->selected = TRUE;
1133 process_menu_window(window, cw)
1137 tty_menu_item *page_start, *page_end, *curr;
1139 int n, curr_page, page_lines;
1140 boolean finished, counting, reset_count;
1141 char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ],
1144 curr_page = page_lines = 0;
1145 page_start = page_end = 0;
1146 msave = cw->morestr; /* save the morestr */
1147 cw->morestr = morestr = (char*) alloc((unsigned) QBUFSZ);
1153 /* collect group accelerators; for PICK_NONE, they're ignored;
1154 for PICK_ONE, only those which match exactly one entry will be
1155 accepted; for PICK_ANY, those which match any entry are okay */
1157 if (cw->how != PICK_NONE) {
1159 #define GSELIDX(c) (c & 127) /* guard against `signed char' */
1161 for (i = 0; i < SIZE(gcnt); i++) gcnt[i] = 0;
1162 for (n = 0, curr = cw->mlist; curr; curr = curr->next)
1163 if (curr->gselector && curr->gselector != curr->selector) {
1165 ++gcnt[GSELIDX(curr->gselector)];
1168 if (n > 0) /* at least one group accelerator found */
1169 for (rp = gacc, curr = cw->mlist; curr; curr = curr->next)
1170 if (curr->gselector && !index(gacc, curr->gselector) &&
1171 (cw->how == PICK_ANY ||
1172 gcnt[GSELIDX(curr->gselector)] == 1)) {
1173 *rp++ = curr->gselector;
1174 *rp = '\0'; /* re-terminate for index() */
1178 /* loop until finished */
1187 /* new page to be displayed */
1188 if (curr_page < 0 || (cw->npages > 0 && curr_page >= cw->npages))
1189 panic("bad menu screen page #%d", curr_page);
1192 if (!cw->offx) { /* if not corner, do clearscreen */
1194 tty_curs(window, 1, 0);
1201 if (cw->npages > 0) {
1202 /* collect accelerators */
1203 page_start = cw->plist[curr_page];
1204 page_end = cw->plist[curr_page + 1];
1205 for (page_lines = 0, curr = page_start;
1207 page_lines++, curr = curr->next) {
1209 *rp++ = curr->selector;
1211 tty_curs(window, 1, page_lines);
1212 if (cw->offx) cl_end();
1214 (void) putchar(' ');
1217 * Don't use xputs() because (1) under unix it calls
1218 * tputstr() which will interpret a '*' as some kind
1219 * of padding information and (2) it calls xputc to
1220 * actually output the character. We're faster doing
1223 term_start_attr(curr->attr);
1224 for (n = 0, cp = curr->str;
1226 *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
1229 *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
1230 cp++, n++, ttyDisplay->curx++)
1232 if (n == 2 && curr->identifier.a_void != 0 &&
1234 if (curr->count == -1L)
1235 (void) putchar('+'); /* all selected */
1237 (void) putchar('#'); /* count selected */
1239 (void) putchar(*cp);
1240 term_end_attr(curr->attr);
1249 /* corner window - clear extra lines from last page */
1251 for (n = page_lines + 1; n < cw->maxrow; n++) {
1252 tty_curs(window, 1, n);
1257 /* set extra chars.. */
1258 Strcat(resp, default_menu_cmds);
1259 Strcat(resp, "0123456789\033\n\r"); /* counts, quit */
1260 Strcat(resp, gacc); /* group accelerators */
1261 Strcat(resp, mapped_menu_cmds);
1264 Sprintf(cw->morestr, "(%d of %d)",
1265 curr_page + 1, (int) cw->npages);
1267 Strcpy(cw->morestr, msave);
1269 Strcpy(cw->morestr, defmorestr);
1271 tty_curs(window, 1, page_lines);
1275 /* just put the cursor back... */
1276 tty_curs(window, (int) strlen(cw->morestr) + 2, page_lines);
1277 xwaitforspace(resp);
1280 morc = map_menu_cmd(morc);
1283 /* special case: '0' is also the default ball class */
1284 if (!counting && index(gacc, morc)) goto group_accel;
1285 /* fall through to count the zero */
1286 case '1': case '2': case '3': case '4':
1287 case '5': case '6': case '7': case '8': case '9':
1288 count = (count * 10L) + (long) (morc - '0');
1290 * It is debatable whether we should allow 0 to
1291 * start a count. There is no difference if the
1292 * item is selected. If not selected, then
1295 * count starting zero: "zero b's"
1296 * ignore starting zero: "select b"
1298 * At present I don't know which is better.
1300 if (count != 0L) { /* ignore leading zeros */
1302 reset_count = FALSE;
1305 case '\033': /* cancel - from counting or loop */
1307 /* deselect everything */
1308 for (curr = cw->mlist; curr; curr = curr->next) {
1309 curr->selected = FALSE;
1312 cw->flags |= WIN_CANCELLED;
1315 /* else only stop count */
1317 case '\0': /* finished (commit) */
1320 /* only finished if we are actually picking something */
1321 if (cw->how != PICK_NONE) {
1325 /* else fall through */
1326 case MENU_NEXT_PAGE:
1327 if (cw->npages > 0 && curr_page != cw->npages - 1) {
1331 finished = TRUE; /* questionable behavior */
1333 case MENU_PREVIOUS_PAGE:
1334 if (cw->npages > 0 && curr_page != 0) {
1339 case MENU_FIRST_PAGE:
1340 if (cw->npages > 0 && curr_page != 0) {
1345 case MENU_LAST_PAGE:
1346 if (cw->npages > 0 && curr_page != cw->npages - 1) {
1348 curr_page = cw->npages - 1;
1351 case MENU_SELECT_PAGE:
1352 if (cw->how == PICK_ANY)
1353 set_all_on_page(window, page_start, page_end);
1355 case MENU_UNSELECT_PAGE:
1356 unset_all_on_page(window, page_start, page_end);
1358 case MENU_INVERT_PAGE:
1359 if (cw->how == PICK_ANY)
1360 invert_all_on_page(window, page_start, page_end, 0);
1362 case MENU_SELECT_ALL:
1363 if (cw->how == PICK_ANY) {
1364 set_all_on_page(window, page_start, page_end);
1366 for (curr = cw->mlist; curr; curr = curr->next)
1367 if (curr->identifier.a_void && !curr->selected)
1368 curr->selected = TRUE;
1371 case MENU_UNSELECT_ALL:
1372 unset_all_on_page(window, page_start, page_end);
1373 /* unset the rest */
1374 for (curr = cw->mlist; curr; curr = curr->next)
1375 if (curr->identifier.a_void && curr->selected) {
1376 curr->selected = FALSE;
1380 case MENU_INVERT_ALL:
1381 if (cw->how == PICK_ANY)
1382 invert_all(window, page_start, page_end, 0);
1385 if (cw->how == PICK_NONE || !index(resp, morc)) {
1386 /* unacceptable input received */
1389 } else if (index(gacc, morc)) {
1391 /* group accelerator; for the PICK_ONE case, we know that
1392 it matches exactly one item in order to be in gacc[] */
1393 invert_all(window, page_start, page_end, morc);
1394 if (cw->how == PICK_ONE) finished = TRUE;
1397 /* find, toggle, and possibly update */
1398 for (n = 0, curr = page_start;
1400 n++, curr = curr->next)
1401 if (morc == curr->selector) {
1402 if (curr->selected) {
1403 if (counting && count > 0) {
1404 curr->count = count;
1405 set_item_state(window, n, curr);
1406 } else { /* change state */
1407 curr->selected = FALSE;
1409 set_item_state(window, n, curr);
1411 } else { /* !selected */
1412 if (counting && count > 0) {
1413 curr->count = count;
1414 curr->selected = TRUE;
1415 set_item_state(window, n, curr);
1416 } else if (!counting) {
1417 curr->selected = TRUE;
1418 set_item_state(window, n, curr);
1420 /* do nothing counting&&count==0 */
1423 if (cw->how == PICK_ONE) finished = TRUE;
1424 break; /* from `for' loop */
1430 cw->morestr = msave;
1431 free((genericptr_t)morestr);
1435 process_text_window(window, cw)
1442 for (n = 0, i = 0; i < cw->maxrow; i++) {
1443 if (!cw->offx && (n + cw->offy == ttyDisplay->rows - 1)) {
1444 tty_curs(window, 1, n);
1446 dmore(cw, quitchars);
1447 if (morc == '\033') {
1448 cw->flags |= WIN_CANCELLED;
1452 tty_curs(window, 1, 0);
1458 tty_curs(window, 1, n++);
1459 if (cw->offx) cl_end();
1461 attr = cw->data[i][0] - 1;
1463 (void) putchar(' '); ++ttyDisplay->curx;
1465 term_start_attr(attr);
1466 for (cp = &cw->data[i][1];
1468 *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
1471 *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
1472 cp++, ttyDisplay->curx++)
1474 (void) putchar(*cp);
1475 term_end_attr(attr);
1478 if (i == cw->maxrow) {
1479 tty_curs(BASE_WINDOW, (int)cw->offx + 1,
1480 (cw->type == NHW_TEXT) ? (int) ttyDisplay->rows - 1 : n);
1482 dmore(cw, quitchars);
1484 cw->flags |= WIN_CANCELLED;
1490 tty_display_nhwindow(window, blocking)
1492 boolean blocking; /* with ttys, all windows are blocking */
1494 register struct WinDesc *cw = 0;
1496 if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1497 panic(winpanicstr, window);
1498 if(cw->flags & WIN_CANCELLED)
1500 ttyDisplay->lastwin = window;
1501 ttyDisplay->rawprint = 0;
1505 if(ttyDisplay->toplin == 1) {
1507 ttyDisplay->toplin = 1; /* more resets this */
1508 tty_clear_nhwindow(window);
1510 ttyDisplay->toplin = 0;
1511 cw->curx = cw->cury = 0;
1513 iflags.window_inited = TRUE;
1518 if(!ttyDisplay->toplin) ttyDisplay->toplin = 1;
1519 tty_display_nhwindow(WIN_MESSAGE, TRUE);
1523 (void) fflush(stdout);
1526 cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
1530 /* avoid converting to uchar before calculations are finished */
1531 cw->offx = (uchar) (int)
1532 max((int) 10, (int) (ttyDisplay->cols - cw->maxcol - 1));
1533 if(cw->type == NHW_MENU)
1535 if(ttyDisplay->toplin == 1)
1536 tty_display_nhwindow(WIN_MESSAGE, TRUE);
1537 if(cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows) {
1540 tty_curs(window, 1, 0);
1544 ttyDisplay->toplin = 0;
1546 tty_clear_nhwindow(WIN_MESSAGE);
1548 if (cw->data || !cw->maxrow)
1549 process_text_window(window, cw);
1551 process_menu_window(window, cw);
1558 tty_dismiss_nhwindow(window)
1561 register struct WinDesc *cw = 0;
1563 if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1564 panic(winpanicstr, window);
1568 if (ttyDisplay->toplin)
1569 tty_display_nhwindow(WIN_MESSAGE, TRUE);
1575 * these should only get dismissed when the game is going away
1578 tty_curs(BASE_WINDOW, 1, (int)ttyDisplay->rows-1);
1584 if (iflags.window_inited) {
1585 /* otherwise dismissing the text endwin after other windows
1586 * are dismissed tries to redraw the map and panics. since
1587 * the whole reason for dismissing the other windows was to
1588 * leave the ending window on the screen, we don't want to
1591 erase_menu_or_text(window, cw, FALSE);
1601 tty_destroy_nhwindow(window)
1604 register struct WinDesc *cw = 0;
1606 if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1607 panic(winpanicstr, window);
1610 tty_dismiss_nhwindow(window);
1611 if(cw->type == NHW_MESSAGE)
1612 iflags.window_inited = 0;
1613 if(cw->type == NHW_MAP)
1616 free_window_info(cw, TRUE);
1617 free((genericptr_t)cw);
1622 tty_curs(window, x, y)
1624 register int x, y; /* not xchar: perhaps xchar is unsigned and
1625 curx-x would be unsigned as well */
1627 struct WinDesc *cw = 0;
1628 int cx = ttyDisplay->curx;
1629 int cy = ttyDisplay->cury;
1631 if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1632 panic(winpanicstr, window);
1633 ttyDisplay->lastwin = window;
1635 #if defined(USE_TILES) && defined(MSDOS)
1636 adjust_cursor_flags(cw);
1638 cw->curx = --x; /* column 0 is never used */
1641 if(x<0 || y<0 || y >= cw->rows || x > cw->cols) {
1642 const char *s = "[unknown type]";
1644 case NHW_MESSAGE: s = "[topl window]"; break;
1645 case NHW_STATUS: s = "[status window]"; break;
1646 case NHW_MAP: s = "[map window]"; break;
1647 case NHW_MENU: s = "[corner window]"; break;
1648 case NHW_TEXT: s = "[text window]"; break;
1649 case NHW_BASE: s = "[base window]"; break;
1651 impossible("bad curs positioning win %d %s (%d,%d)", window, s, x, y);
1659 if(clipping && window == WIN_MAP) {
1665 if (y == cy && x == cx)
1668 if(cw->type == NHW_MAP)
1672 if(!nh_ND && (cx != x || x <= 3)) { /* Extremely primitive */
1673 cmov(x, y); /* bunker!wtm */
1678 if((cy -= y) < 0) cy = -cy;
1679 if((cx -= x) < 0) cx = -cx;
1680 if(cy <= 3 && cx <= 3) {
1683 } else if ((x <= 3 && cy <= 3) || (!nh_CM && x < cx)) {
1684 (void) putchar('\r');
1685 ttyDisplay->curx = 0;
1687 } else if (!nh_CM) {
1693 ttyDisplay->curx = x;
1694 ttyDisplay->cury = y;
1698 tty_putsym(window, x, y, ch)
1703 register struct WinDesc *cw = 0;
1705 if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1706 panic(winpanicstr, window);
1712 tty_curs(window, x, y);
1720 impossible("Can't putsym to window type %d", cw->type);
1726 STATIC_OVL const char*
1730 static char cbuf[BUFSZ];
1731 /* compress in case line too long */
1732 if((int)strlen(str) >= CO) {
1733 register const char *bp0 = str;
1734 register char *bp1 = cbuf;
1738 if(*bp0 != ' ' || bp0[1] != ' ')
1740 if(*bp0 != ' ' || bp0[1] != ' ' || bp0[2] != ' ')
1750 tty_putstr(window, attr, str)
1755 register struct WinDesc *cw = 0;
1757 register const char *nb;
1758 register int i, j, n0;
1760 /* Assume there's a real problem if the window is missing --
1761 * probably a panic message
1763 if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) {
1768 if(str == (const char*)0 ||
1769 ((cw->flags & WIN_CANCELLED) && (cw->type != NHW_MESSAGE)))
1771 if(cw->type != NHW_MESSAGE)
1772 str = compress_str(str);
1774 ttyDisplay->lastwin = window;
1778 /* really do this later */
1779 #if defined(USER_SOUNDS) && defined(WIN32CON)
1780 play_sound_for_message(str);
1786 ob = &cw->data[cw->cury][j = cw->curx];
1787 if(flags.botlx) *ob = 0;
1788 if(!cw->cury && (int)strlen(str) >= CO) {
1789 /* the characters before "St:" are unnecessary */
1790 nb = index(str, ':');
1791 if(nb && nb > str+2)
1795 for(i = cw->curx+1, n0 = cw->cols; i < n0; i++, nb++) {
1797 if(*ob || flags.botlx) {
1798 /* last char printed may be in middle of line */
1799 tty_curs(WIN_STATUS, i, cw->cury);
1805 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
1809 (void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1);
1810 cw->data[cw->cury][cw->cols-1] = '\0'; /* null terminate */
1811 cw->cury = (cw->cury+1) % 2;
1815 tty_curs(window, cw->curx+1, cw->cury);
1816 term_start_attr(attr);
1817 while(*str && (int) ttyDisplay->curx < (int) ttyDisplay->cols-1) {
1818 (void) putchar(*str);
1824 term_end_attr(attr);
1827 tty_curs(window, cw->curx+1, cw->cury);
1828 term_start_attr(attr);
1830 if ((int) ttyDisplay->curx >= (int) ttyDisplay->cols-1) {
1833 tty_curs(window, cw->curx+1, cw->cury);
1835 (void) putchar(*str);
1841 term_end_attr(attr);
1845 if(cw->type == NHW_TEXT && cw->cury == ttyDisplay->rows-1) {
1846 /* not a menu, so save memory and output 1 page at a time */
1847 cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
1848 tty_display_nhwindow(window, TRUE);
1849 for(i=0; i<cw->maxrow; i++)
1851 free((genericptr_t)cw->data[i]);
1854 cw->maxrow = cw->cury = 0;
1856 /* always grows one at a time, but alloc 12 at a time */
1857 if(cw->cury >= cw->rows) {
1861 tmp = (char **) alloc(sizeof(char *) * (unsigned)cw->rows);
1862 for(i=0; i<cw->maxrow; i++)
1863 tmp[i] = cw->data[i];
1865 free((genericptr_t)cw->data);
1868 for(i=cw->maxrow; i<cw->rows; i++)
1871 if(cw->data[cw->cury])
1872 free((genericptr_t)cw->data[cw->cury]);
1873 n0 = strlen(str) + 1;
1874 ob = cw->data[cw->cury] = (char *)alloc((unsigned)n0 + 1);
1875 *ob++ = (char)(attr + 1); /* avoid nuls, for convenience */
1880 if(++cw->cury > cw->maxrow)
1881 cw->maxrow = cw->cury;
1883 /* attempt to break the line */
1884 for(i = CO-1; i && str[i] != ' ' && str[i] != '\n';)
1887 cw->data[cw->cury-1][++i] = '\0';
1888 tty_putstr(window, attr, &str[i]);
1897 tty_display_file(fname, complain)
1901 #ifdef DEF_PAGER /* this implies that UNIX is defined */
1903 /* use external pager; this may give security problems */
1904 register int fd = open(fname, 0);
1907 if(complain) pline("Cannot open %s.", fname);
1912 /* Now that child() does a setuid(getuid()) and a chdir(),
1913 we may not be able to open file fname anymore, so make
1917 if(complain) raw_printf("Cannot open %s as stdin.", fname);
1919 (void) execlp(catmore, "page", (char *)0);
1920 if(complain) raw_printf("Cannot exec %s.", catmore);
1922 if(complain) sleep(10); /* want to wait_synch() but stdin is gone */
1923 terminate(EXIT_FAILURE);
1927 #else /* DEF_PAGER */
1933 tty_clear_nhwindow(WIN_MESSAGE);
1934 f = dlb_fopen(fname, "r");
1937 home(); tty_mark_synch(); tty_raw_print("");
1938 perror(fname); tty_wait_synch();
1939 pline("Cannot open \"%s\".", fname);
1940 } else if(u.ux) docrt();
1942 winid datawin = tty_create_nhwindow(NHW_TEXT);
1943 boolean empty = TRUE;
1950 /* attempt to scroll text below map window if there's room */
1951 wins[datawin]->offy = wins[WIN_STATUS]->offy+3;
1952 if((int) wins[datawin]->offy + 12 > (int) ttyDisplay->rows)
1953 wins[datawin]->offy = 0;
1955 while (dlb_fgets(buf, BUFSZ, f)) {
1956 if ((cr = index(buf, '\n')) != 0) *cr = 0;
1958 if ((cr = index(buf, '\r')) != 0) *cr = 0;
1960 if (index(buf, '\t') != 0) (void) tabexpand(buf);
1962 tty_putstr(datawin, 0, buf);
1963 if(wins[datawin]->flags & WIN_CANCELLED)
1966 if (!empty) tty_display_nhwindow(datawin, FALSE);
1967 tty_destroy_nhwindow(datawin);
1968 (void) dlb_fclose(f);
1971 #endif /* DEF_PAGER */
1975 tty_start_menu(window)
1978 tty_clear_nhwindow(window);
1984 * Add a menu item to the beginning of the menu list. This list is reversed
1988 tty_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
1989 winid window; /* window to use, must be of type NHW_MENU */
1990 int glyph; /* glyph to display with item (unused) */
1991 const anything *identifier; /* what to return if selected */
1992 char ch; /* keyboard accelerator (0 = pick our own) */
1993 char gch; /* group accelerator (0 = no group) */
1994 int attr; /* attribute for string (like tty_putstr()) */
1995 const char *str; /* menu string */
1996 boolean preselected; /* item is marked as selected */
1998 register struct WinDesc *cw = 0;
1999 tty_menu_item *item;
2003 if (str == (const char*) 0)
2006 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
2007 || cw->type != NHW_MENU)
2008 panic(winpanicstr, window);
2011 if (identifier->a_void) {
2012 int len = strlen(str);
2014 /* We *think* everything's coming in off at most BUFSZ bufs... */
2015 impossible("Menu item too long (%d).", len);
2018 Sprintf(buf, "%c - ", ch ? ch : '?');
2019 (void) strncpy(buf+4, str, len);
2025 item = (tty_menu_item *) alloc(sizeof(tty_menu_item));
2026 item->identifier = *identifier;
2028 item->selected = preselected;
2029 item->selector = ch;
2030 item->gselector = gch;
2032 item->str = copy_of(newstr);
2034 item->next = cw->mlist;
2038 /* Invert the given list, can handle NULL as an input. */
2039 STATIC_OVL tty_menu_item *
2041 tty_menu_item *curr;
2043 tty_menu_item *next, *head = 0;
2055 * End a menu in this window, window must a type NHW_MENU. This routine
2056 * processes the string list. We calculate the # of pages, then assign
2057 * keyboard accelerators as needed. Finally we decide on the width and
2058 * height of the window.
2061 tty_end_menu(window, prompt)
2062 winid window; /* menu to use */
2063 const char *prompt; /* prompt to for menu */
2065 struct WinDesc *cw = 0;
2066 tty_menu_item *curr;
2071 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0 ||
2072 cw->type != NHW_MENU)
2073 panic(winpanicstr, window);
2075 /* Reverse the list so that items are in correct order. */
2076 cw->mlist = reverse(cw->mlist);
2078 /* Put the promt at the beginning of the menu. */
2082 any.a_void = 0; /* not selectable */
2083 tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
2084 tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt, MENU_UNSELECTED);
2087 lmax = min(52, (int)ttyDisplay->rows - 1); /* # lines per page */
2088 cw->npages = (cw->nitems + (lmax - 1)) / lmax; /* # of pages */
2090 /* make sure page list is large enough */
2091 if (cw->plist_size < cw->npages+1 /*need 1 slot beyond last*/) {
2092 if (cw->plist) free((genericptr_t)cw->plist);
2093 cw->plist_size = cw->npages + 1;
2094 cw->plist = (tty_menu_item **)
2095 alloc(cw->plist_size * sizeof(tty_menu_item *));
2098 cw->cols = 0; /* cols is set when the win is initialized... (why?) */
2099 menu_ch = '?'; /* lint suppression */
2100 for (n = 0, curr = cw->mlist; curr; n++, curr = curr->next) {
2101 /* set page boundaries and character accelerators */
2102 if ((n % lmax) == 0) {
2104 cw->plist[n/lmax] = curr;
2106 if (curr->identifier.a_void && !curr->selector) {
2107 curr->str[0] = curr->selector = menu_ch;
2108 if (menu_ch++ == 'z') menu_ch = 'A';
2111 /* cut off any lines that are too long */
2112 len = strlen(curr->str) + 2; /* extra space at beg & end */
2113 if (len > (int)ttyDisplay->cols) {
2114 curr->str[ttyDisplay->cols-2] = 0;
2115 len = ttyDisplay->cols;
2117 if (len > cw->cols) cw->cols = len;
2119 cw->plist[cw->npages] = 0; /* plist terminator */
2122 * If greater than 1 page, morestr is "(x of y) " otherwise, "(end) "
2124 if (cw->npages > 1) {
2126 /* produce the largest demo string */
2127 Sprintf(buf, "(%d of %d) ", cw->npages, cw->npages);
2129 cw->morestr = copy_of("");
2131 cw->morestr = copy_of("(end) ");
2132 len = strlen(cw->morestr);
2135 if (len > (int)ttyDisplay->cols) {
2136 /* truncate the prompt if its too long for the screen */
2137 if (cw->npages <= 1) /* only str in single page case */
2138 cw->morestr[ttyDisplay->cols] = 0;
2139 len = ttyDisplay->cols;
2141 if (len > cw->cols) cw->cols = len;
2143 cw->maxcol = cw->cols;
2146 * The number of lines in the first page plus the morestr will be the
2147 * maximum size of the window.
2150 cw->maxrow = cw->rows = lmax + 1;
2152 cw->maxrow = cw->rows = cw->nitems + 1;
2156 tty_select_menu(window, how, menu_list)
2159 menu_item **menu_list;
2161 register struct WinDesc *cw = 0;
2162 tty_menu_item *curr;
2166 if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
2167 || cw->type != NHW_MENU)
2168 panic(winpanicstr, window);
2170 *menu_list = (menu_item *) 0;
2171 cw->how = (short) how;
2173 tty_display_nhwindow(window, TRUE);
2174 cancelled = !!(cw->flags & WIN_CANCELLED);
2175 tty_dismiss_nhwindow(window); /* does not destroy window data */
2180 for (n = 0, curr = cw->mlist; curr; curr = curr->next)
2181 if (curr->selected) n++;
2185 *menu_list = (menu_item *) alloc(n * sizeof(menu_item));
2186 for (mi = *menu_list, curr = cw->mlist; curr; curr = curr->next)
2187 if (curr->selected) {
2188 mi->item = curr->identifier;
2189 mi->count = curr->count;
2197 /* special hack for treating top line --More-- as a one item menu */
2199 tty_message_menu(let, how, mesg)
2204 /* "menu" without selection; use ordinary pline, no more() */
2205 if (how == PICK_NONE) {
2210 ttyDisplay->dismiss_more = let;
2212 /* barebones pline(); since we're only supposed to be called after
2213 response to a prompt, we'll assume that the display is up to date */
2214 tty_putstr(WIN_MESSAGE, 0, mesg);
2215 /* if `mesg' didn't wrap (triggering --More--), force --More-- now */
2216 if (ttyDisplay->toplin == 1) {
2218 ttyDisplay->toplin = 1; /* more resets this */
2219 tty_clear_nhwindow(WIN_MESSAGE);
2221 /* normally <ESC> means skip further messages, but in this case
2222 it means cancel the current prompt; any other messages should
2223 continue to be output normally */
2224 wins[WIN_MESSAGE]->flags &= ~WIN_CANCELLED;
2225 ttyDisplay->dismiss_more = 0;
2227 return ((how == PICK_ONE && morc == let) || morc == '\033') ? morc : '\0';
2231 tty_update_inventory()
2239 (void) fflush(stdout);
2245 /* we just need to make sure all windows are synch'd */
2246 if(!ttyDisplay || ttyDisplay->rawprint) {
2248 if(ttyDisplay) ttyDisplay->rawprint = 0;
2250 tty_display_nhwindow(WIN_MAP, FALSE);
2251 if(ttyDisplay->inmore) {
2252 addtopl("--More--");
2253 (void) fflush(stdout);
2254 } else if(ttyDisplay->inread > program_state.gameover) {
2255 /* this can only happen if we were reading and got interrupted */
2256 ttyDisplay->toplin = 3;
2257 /* do this twice; 1st time gets the Quit? message again */
2258 (void) tty_doprev_message();
2259 (void) tty_doprev_message();
2261 (void) fflush(stdout);
2267 docorner(xmin, ymax)
2268 register int xmin, ymax;
2271 register struct WinDesc *cw = wins[WIN_MAP];
2273 if (u.uswallow) { /* Can be done more efficiently */
2278 #if defined(SIGWINCH) && defined(CLIPPING)
2279 if(ymax > LI) ymax = LI; /* can happen if window gets smaller */
2281 for (y = 0; y < ymax; y++) {
2282 tty_curs(BASE_WINDOW, xmin,y); /* move cursor */
2283 cl_end(); /* clear to end of line */
2285 if (y<(int) cw->offy || y+clipy > ROWNO)
2286 continue; /* only refresh board */
2287 #if defined(USE_TILES) && defined(MSDOS)
2288 if (iflags.tile_view)
2289 row_refresh((xmin/2)+clipx-((int)cw->offx/2),COLNO-1,y+clipy-(int)cw->offy);
2292 row_refresh(xmin+clipx-(int)cw->offx,COLNO-1,y+clipy-(int)cw->offy);
2294 if (y<cw->offy || y > ROWNO) continue; /* only refresh board */
2295 row_refresh(xmin-(int)cw->offx,COLNO-1,y-(int)cw->offy);
2300 if (ymax >= (int) wins[WIN_STATUS]->offy) {
2301 /* we have wrecked the bottom line */
2310 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
2317 if(ttyDisplay->color != NO_COLOR) {
2319 ttyDisplay->color = NO_COLOR;
2329 register char ch = (char)in_ch;
2331 # if defined(ASCIIGRAPH) && !defined(NO_TERMS)
2332 if (iflags.IBMgraphics || iflags.eight_bit_tty) {
2333 /* IBM-compatible displays don't need other stuff */
2335 } else if (ch & 0x80) {
2336 if (!GFlag || HE_resets_AS) {
2340 (void) putchar((ch ^ 0x80)); /* Strip 8th bit */
2352 #endif /* ASCIIGRAPH && !NO_TERMS */
2369 tty_cliparound(x, y)
2372 extern boolean restoring;
2373 int oldx = clipx, oldy = clipy;
2375 if (!clipping) return;
2376 if (x < clipx + 5) {
2377 clipx = max(0, x - 20);
2378 clipxmax = clipx + CO;
2380 else if (x > clipxmax - 5) {
2381 clipxmax = min(COLNO, clipxmax + 20);
2382 clipx = clipxmax - CO;
2384 if (y < clipy + 2) {
2385 clipy = max(0, y - (clipymax - clipy) / 2);
2386 clipymax = clipy + (LI - 3);
2388 else if (y > clipymax - 2) {
2389 clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2);
2390 clipy = clipymax - (LI - 3);
2392 if (clipx != oldx || clipy != oldy) {
2393 if (on_level(&u.uz0, &u.uz) && !restoring)
2397 #endif /* CLIPPING */
2403 * Print the glyph to the output device. Don't flush the output device.
2405 * Since this is only called from show_glyph(), it is assumed that the
2406 * position and glyph are always correct (checked there)!
2410 tty_print_glyph(window, x, y, glyph)
2416 boolean reverse_on = FALSE;
2422 if(x <= clipx || y < clipy || x >= clipxmax || y >= clipymax)
2426 /* map glyph to character and color */
2427 mapglyph(glyph, &ch, &color, &special, x, y);
2429 /* Move the cursor. */
2430 tty_curs(window, x,y);
2433 if (ul_hack && ch == '_') { /* non-destructive underscore */
2434 (void) putchar((char) ' ');
2440 if (color != ttyDisplay->color) {
2441 if(ttyDisplay->color != NO_COLOR)
2443 ttyDisplay->color = color;
2444 if(color != NO_COLOR)
2445 term_start_color(color);
2447 #endif /* TEXTCOLOR */
2449 /* must be after color check; term_end_color may turn off inverse too */
2450 if (((special & MG_PET) && iflags.hilite_pet) ||
2451 ((special & MG_DETECT) && iflags.use_inverse)) {
2452 term_start_attr(ATR_INVERSE);
2456 #if defined(USE_TILES) && defined(MSDOS)
2457 if (iflags.grmode && iflags.tile_view)
2458 xputg(glyph,ch,special);
2461 g_putch(ch); /* print the character */
2464 term_end_attr(ATR_INVERSE);
2466 /* turn off color as well, ATR_INVERSE may have done this already */
2467 if(ttyDisplay->color != NO_COLOR) {
2469 ttyDisplay->color = NO_COLOR;
2474 wins[window]->curx++; /* one character over */
2475 ttyDisplay->curx++; /* the real cursor moved too */
2482 if(ttyDisplay) ttyDisplay->rawprint++;
2483 #if defined(MICRO) || defined(WIN32CON)
2486 puts(str); (void) fflush(stdout);
2491 tty_raw_print_bold(str)
2494 if(ttyDisplay) ttyDisplay->rawprint++;
2495 term_start_raw_bold();
2496 #if defined(MICRO) || defined(WIN32CON)
2499 (void) fputs(str, stdout);
2501 term_end_raw_bold();
2502 #if defined(MICRO) || defined(WIN32CON)
2506 (void) fflush(stdout);
2515 /* kludge alert: Some Unix variants return funny values if getc()
2516 * is called, interrupted, and then called again. There
2517 * is non-reentrant code in the internal _filbuf() routine, called by
2520 static volatile int nesting = 0;
2524 (void) fflush(stdout);
2525 /* Note: if raw_print() and wait_synch() get called to report terminal
2526 * initialization problems, then wins[] and ttyDisplay might not be
2527 * available yet. Such problems will probably be fatal before we get
2528 * here, but validate those pointers just in case...
2530 if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
2531 wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
2533 i = ((++nesting == 1) ? tgetch() :
2534 (read(fileno(stdin), (genericptr_t)&nestbuf,1) == 1 ? (int)nestbuf :
2540 if (!i) i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
2541 if (ttyDisplay && ttyDisplay->toplin == 1)
2542 ttyDisplay->toplin = 2;
2547 * return a key, or 0, in which case a mouse button was pressed
2548 * mouse events should be returned as character postitions in the map window.
2549 * Since normal tty's don't have mice, just return a key.
2553 tty_nh_poskey(x, y, mod)
2556 # if defined(WIN32CON)
2558 (void) fflush(stdout);
2559 /* Note: if raw_print() and wait_synch() get called to report terminal
2560 * initialization problems, then wins[] and ttyDisplay might not be
2561 * available yet. Such problems will probably be fatal before we get
2562 * here, but validate those pointers just in case...
2564 if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
2565 wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
2566 i = ntposkey(x, y, mod);
2567 if (!i && mod && *mod == 0)
2568 i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
2569 if (ttyDisplay && ttyDisplay->toplin == 1)
2570 ttyDisplay->toplin = 2;
2573 return tty_nhgetch();
2580 # if defined(WIN32CON)
2588 tty_update_positionbar(posbar)
2592 video_update_positionbar(posbar);
2598 * Allocate a copy of the given string. If null, return a string of
2601 * This is an exact duplicate of copy_of() in X11/winmenu.c.
2608 return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s);
2611 #endif /* TTY_GRAPHICS */