1    | /*	SCCS Id: @(#)wintty.c	3.3	2000/06/27	*/
2    | /* Copyright (c) David Cohrs, 1991				  */
3    | /* NetHack may be freely redistributed.  See license for details. */
4    | 
5    | /*
6    |  * Neither a standard out nor character-based control codes should be
7    |  * part of the "tty look" windowing implementation.
8    |  * h+ 930227
9    |  */
10   | 
11   | #include "hack.h"
12   | #include "dlb.h"
13   | #ifdef SHORT_FILENAMES
14   | #include "patchlev.h"
15   | #else
16   | #include "patchlevel.h"
17   | #endif
18   | 
19   | #ifdef TTY_GRAPHICS
20   | 
21   | #ifdef MAC
22   | # define MICRO /* The Mac is a MICRO only for this file, not in general! */
23   | # ifdef THINK_C
24   | extern void msmsg(const char *,...);
25   | # endif
26   | #endif
27   | 
28   | 
29   | #ifndef NO_TERMS
30   | #include "tcap.h"
31   | #endif
32   | 
33   | #include "wintty.h"
34   | 
35   | #ifdef CLIPPING		/* might want SIGWINCH */
36   | # if defined(BSD) || defined(ULTRIX) || defined(AIX_31) || defined(_BULL_SOURCE)
37   | #include <signal.h>
38   | # endif
39   | #endif
40   | 
41   | #define DEBUG
42   | 
43   | extern char mapped_menu_cmds[]; /* from options.c */
44   | 
45   | /* Interface definition, for windows.c */
46   | struct window_procs tty_procs = {
47   |     "tty",
48   |     tty_init_nhwindows,
49   |     tty_player_selection,
50   |     tty_askname,
51   |     tty_get_nh_event,
52   |     tty_exit_nhwindows,
53   |     tty_suspend_nhwindows,
54   |     tty_resume_nhwindows,
55   |     tty_create_nhwindow,
56   |     tty_clear_nhwindow,
57   |     tty_display_nhwindow,
58   |     tty_destroy_nhwindow,
59   |     tty_curs,
60   |     tty_putstr,
61   |     tty_display_file,
62   |     tty_start_menu,
63   |     tty_add_menu,
64   |     tty_end_menu,
65   |     tty_select_menu,
66   |     tty_message_menu,
67   |     tty_update_inventory,
68   |     tty_mark_synch,
69   |     tty_wait_synch,
70   | #ifdef CLIPPING
71   |     tty_cliparound,
72   | #endif
73   | #ifdef POSITIONBAR
74   |     tty_update_positionbar,
75   | #endif
76   |     tty_print_glyph,
77   |     tty_raw_print,
78   |     tty_raw_print_bold,
79   |     tty_nhgetch,
80   |     tty_nh_poskey,
81   |     tty_nhbell,
82   |     tty_doprev_message,
83   |     tty_yn_function,
84   |     tty_getlin,
85   |     tty_get_ext_cmd,
86   |     tty_number_pad,
87   |     tty_delay_output,
88   | #ifdef CHANGE_COLOR	/* the Mac uses a palette device */
89   |     tty_change_color,
90   | #ifdef MAC
91   |     tty_change_background,
92   |     set_tty_font_name,
93   | #endif
94   |     tty_get_color_string,
95   | #endif
96   | 
97   |     /* other defs that really should go away (they're tty specific) */
98   |     tty_start_screen,
99   |     tty_end_screen,
100  |     genl_outrip
101  | };
102  | 
103  | static int maxwin = 0;			/* number of windows in use */
104  | winid BASE_WINDOW;
105  | struct WinDesc *wins[MAXWIN];
106  | struct DisplayDesc *ttyDisplay;	/* the tty display descriptor */
107  | 
108  | extern void FDECL(cmov, (int,int)); /* from termcap.c */
109  | extern void FDECL(nocmov, (int,int)); /* from termcap.c */
110  | #if defined(UNIX) || defined(VMS)
111  | static char obuf[BUFSIZ];	/* BUFSIZ is defined in stdio.h */
112  | #endif
113  | 
114  | static char winpanicstr[] = "Bad window id %d";
115  | char defmorestr[] = "--More--";
116  | 
117  | #ifdef CLIPPING
118  | # if defined(USE_TILES) && defined(MSDOS)
119  | boolean clipping = FALSE;	/* clipping on? */
120  | int clipx = 0, clipxmax = 0;
121  | # else
122  | static boolean clipping = FALSE;	/* clipping on? */
123  | static int clipx = 0, clipxmax = 0;
124  | # endif
125  | static int clipy = 0, clipymax = 0;
126  | #endif /* CLIPPING */
127  | 
128  | #if defined(USE_TILES) && defined(MSDOS)
129  | extern void FDECL(adjust_cursor_flags, (struct WinDesc *));
130  | #endif
131  | 
132  | #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
133  | boolean GFlag = FALSE;
134  | boolean HE_resets_AS;	/* see termcap.c */
135  | #endif
136  | 
137  | #ifdef MICRO
138  | static char to_continue[] = "to continue";
139  | #define getret() getreturn(to_continue)
140  | #else
141  | STATIC_DCL void NDECL(getret);
142  | #endif
143  | STATIC_DCL void FDECL(erase_menu_or_text, (winid, struct WinDesc *, BOOLEAN_P));
144  | STATIC_DCL void FDECL(free_window_info, (struct WinDesc *, BOOLEAN_P));
145  | STATIC_DCL void FDECL(dmore,(struct WinDesc *, const char *));
146  | STATIC_DCL void FDECL(set_item_state, (winid, int, tty_menu_item *));
147  | STATIC_DCL void FDECL(set_all_on_page, (winid,tty_menu_item *,tty_menu_item *));
148  | STATIC_DCL void FDECL(unset_all_on_page, (winid,tty_menu_item *,tty_menu_item *));
149  | STATIC_DCL void FDECL(invert_all_on_page, (winid,tty_menu_item *,tty_menu_item *, CHAR_P));
150  | STATIC_DCL void FDECL(invert_all, (winid,tty_menu_item *,tty_menu_item *, CHAR_P));
151  | STATIC_DCL void FDECL(process_menu_window, (winid,struct WinDesc *));
152  | STATIC_DCL void FDECL(process_text_window, (winid,struct WinDesc *));
153  | STATIC_DCL tty_menu_item *FDECL(reverse, (tty_menu_item *));
154  | STATIC_DCL const char * FDECL(compress_str, (const char *));
155  | STATIC_DCL void FDECL(tty_putsym, (winid, int, int, CHAR_P));
156  | static char *FDECL(copy_of, (const char *));
157  | STATIC_DCL void FDECL(bail, (const char *));	/* __attribute__((noreturn)) */
158  | 
159  | /*
160  |  * A string containing all the default commands -- to add to a list
161  |  * of acceptable inputs.
162  |  */
163  | static const char default_menu_cmds[] = {
164  | 	MENU_FIRST_PAGE,
165  | 	MENU_LAST_PAGE,
166  | 	MENU_NEXT_PAGE,
167  | 	MENU_PREVIOUS_PAGE,
168  | 	MENU_SELECT_ALL,
169  | 	MENU_UNSELECT_ALL,
170  | 	MENU_INVERT_ALL,
171  | 	MENU_SELECT_PAGE,
172  | 	MENU_UNSELECT_PAGE,
173  | 	MENU_INVERT_PAGE,
174  | 	0	/* null terminator */
175  | };
176  | 
177  | 
178  | /* clean up and quit */
179  | STATIC_OVL void
180  | bail(mesg)
181  | const char *mesg;
182  | {
183  |     clearlocks();
184  |     tty_exit_nhwindows(mesg);
185  |     terminate(EXIT_SUCCESS);
186  |     /*NOTREACHED*/
187  | }
188  | 
189  | #if defined(SIGWINCH) && defined(CLIPPING)
190  | STATIC_OVL void
191  | winch()
192  | {
193  |     int oldLI = LI, oldCO = CO, i;
194  |     register struct WinDesc *cw;
195  | 
196  |     getwindowsz();
197  |     if((oldLI != LI || oldCO != CO) && ttyDisplay) {
198  | 	ttyDisplay->rows = LI;
199  | 	ttyDisplay->cols = CO;
200  | 
201  | 	cw = wins[BASE_WINDOW];
202  | 	cw->rows = ttyDisplay->rows;
203  | 	cw->cols = ttyDisplay->cols;
204  | 
205  | 	if(iflags.window_inited) {
206  | 	    cw = wins[WIN_MESSAGE];
207  | 	    cw->curx = cw->cury = 0;
208  | 
209  | 	    tty_destroy_nhwindow(WIN_STATUS);
210  | 	    WIN_STATUS = tty_create_nhwindow(NHW_STATUS);
211  | 
212  | 	    if(u.ux) {
213  | #ifdef CLIPPING
214  | 		if(CO < COLNO || LI < ROWNO+3) {
215  | 		    setclipped();
216  | 		    tty_cliparound(u.ux, u.uy);
217  | 		} else {
218  | 		    clipping = FALSE;
219  | 		    clipx = clipy = 0;
220  | 		}
221  | #endif
222  | 		i = ttyDisplay->toplin;
223  | 		ttyDisplay->toplin = 0;
224  | 		docrt();
225  | 		bot();
226  | 		ttyDisplay->toplin = i;
227  | 		flush_screen(1);
228  | 		if(i) {
229  | 		    addtopl(toplines);
230  | 		} else
231  | 		    for(i=WIN_INVEN; i < MAXWIN; i++)
232  | 			if(wins[i] && wins[i]->active) {
233  | 			    /* cop-out */
234  | 			    addtopl("Press Return to continue: ");
235  | 			    break;
236  | 			}
237  | 		(void) fflush(stdout);
238  | 		if(i < 2) flush_screen(1);
239  | 	    }
240  | 	}
241  |     }
242  | }
243  | #endif
244  | 
245  | /*ARGSUSED*/
246  | void
247  | tty_init_nhwindows(argcp,argv)
248  | int* argcp;
249  | char** argv;
250  | {
251  |     int wid, hgt;
252  | 
253  |     /*
254  |      *  Remember tty modes, to be restored on exit.
255  |      *
256  |      *  gettty() must be called before tty_startup()
257  |      *    due to ordering of LI/CO settings
258  |      *  tty_startup() must be called before initoptions()
259  |      *    due to ordering of graphics settings
260  |      */
261  | #if defined(UNIX) || defined(VMS)
262  |     setbuf(stdout,obuf);
263  | #endif
264  |     gettty();
265  | 
266  |     /* to port dependant tty setup */
267  |     tty_startup(&wid, &hgt);
268  |     setftty();			/* calls start_screen */
269  | 
270  |     /* set up tty descriptor */
271  |     ttyDisplay = (struct DisplayDesc*) alloc(sizeof(struct DisplayDesc));
272  |     ttyDisplay->toplin = 0;
273  |     ttyDisplay->rows = hgt;
274  |     ttyDisplay->cols = wid;
275  |     ttyDisplay->curx = ttyDisplay->cury = 0;
276  |     ttyDisplay->inmore = ttyDisplay->inread = ttyDisplay->intr = 0;
277  |     ttyDisplay->dismiss_more = 0;
278  | #ifdef TEXTCOLOR
279  |     ttyDisplay->color = NO_COLOR;
280  | #endif
281  |     ttyDisplay->attrs = 0;
282  | 
283  |     /* set up the default windows */
284  |     BASE_WINDOW = tty_create_nhwindow(NHW_BASE);
285  |     wins[BASE_WINDOW]->active = 1;
286  | 
287  |     ttyDisplay->lastwin = WIN_ERR;
288  | 
289  | #if defined(SIGWINCH) && defined(CLIPPING)
290  |     (void) signal(SIGWINCH, winch);
291  | #endif
292  | 
293  |     /* add one a space forward menu command alias */
294  |     add_menu_cmd_alias(' ', MENU_NEXT_PAGE);
295  | 
296  |     tty_clear_nhwindow(BASE_WINDOW);
297  | 
298  |     tty_putstr(BASE_WINDOW, 0, "");
299  |     tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_A);
300  |     tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_B);
301  |     tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_C);
302  |     tty_putstr(BASE_WINDOW, 0, "");
303  |     tty_display_nhwindow(BASE_WINDOW, FALSE);
304  | }
305  | 
306  | void
307  | tty_player_selection()
308  | {
309  | 	int i, k, n;
310  | 	char pick4u = 'n', thisch, lastch = 0;
311  | 	char pbuf[QBUFSZ];
312  | 	winid win;
313  | 	anything any;
314  | 	menu_item *selected = 0;
315  | 
316  | 	/* Should we randomly pick for the player? */
317  | 	if (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE ||
318  | 		flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE) {
319  | 	    const char *prompt = "Shall I pick a character for you? [ynq] ";
320  | 	    int echoline;
321  | 
322  | 	    tty_putstr(BASE_WINDOW, 0, "");
323  | 	    echoline = wins[BASE_WINDOW]->cury;
324  | 	    tty_putstr(BASE_WINDOW, 0, prompt);
325  | 	    do {
326  | 		pick4u = lowc(readchar());
327  | 		if (index(quitchars, pick4u)) pick4u = 'y';
328  | 	    } while(!index(ynqchars, pick4u));
329  | 	    if ((int)strlen(prompt) + 1 < CO) {
330  | 		/* Echo choice and move back down line */
331  | 		tty_putsym(BASE_WINDOW, (int)strlen(prompt)+1, echoline, pick4u);
332  | 		tty_putstr(BASE_WINDOW, 0, "");
333  | 	    } else
334  | 		/* Otherwise it's hard to tell where to echo, and things are
335  | 		 * wrapping a bit messily anyway, so (try to) make sure the next
336  | 		 * question shows up well and doesn't get wrapped at the
337  | 		 * bottom of the window.
338  | 		 */
339  | 		tty_clear_nhwindow(BASE_WINDOW);
340  | 	    
341  | 	    if (pick4u != 'y' && pick4u != 'n') {
342  | give_up:	/* Quit */
343  | 		if (selected) free((genericptr_t) selected);
344  | 		bail((char *)0);
345  | 		/*NOTREACHED*/
346  | 		return;
347  | 	    }
348  | 	}
349  | 
350  | 	/* Select a role, if necessary */
351  | 	/* we'll try to be compatible with pre-selected race/gender/alignment,
352  | 	 * but may not succeed */
353  | 	if (flags.initrole < 0) {
354  | 	    /* Process the choice */
355  | 	    if (pick4u == 'y' || flags.initrole == ROLE_RANDOM) {
356  | 		/* Pick a random role */
357  | 		flags.initrole = pick_role(flags.initrace, flags.initgend,
358  | 						flags.initalign);
359  | 		if (flags.initrole < 0) {
360  | 		    tty_putstr(BASE_WINDOW, 0, "Incompatible role!");
361  | 		    flags.initrole = randrole();
362  | 		}
363  | 	    } else {
364  | 		/* Prompt for a role */
365  | 		win = create_nhwindow(NHW_MENU);
366  | 		start_menu(win);
367  | 		any.a_void = 0;         /* zero out all bits */
368  | 		for (i = 0; roles[i].name.m; i++) {
369  | 		    if (ok_role(i, flags.initrace, flags.initgend,
370  | 							flags.initalign)) {
371  | 			any.a_int = i+1;	/* must be non-zero */
372  | 			thisch = lowc(roles[i].name.m[0]);
373  | 			if (thisch == lastch) thisch = highc(thisch);
374  | 			add_menu(win, NO_GLYPH, &any, thisch,
375  | 			    0, ATR_NONE, an(roles[i].name.m), MENU_UNSELECTED);
376  | 			lastch = thisch;
377  | 		    }
378  | 		}
379  | 		any.a_int = pick_role(flags.initrace, flags.initgend,
380  | 				    flags.initalign)+1;
381  | 		if (any.a_int == 0)	/* must be non-zero */
382  | 		    any.a_int = randrole()+1;
383  | 		add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
384  | 				"Random", MENU_UNSELECTED);
385  | 		any.a_int = i+1;	/* must be non-zero */
386  | 		add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
387  | 				"Quit", MENU_UNSELECTED);
388  | 		end_menu(win, "Pick a role");
389  | 		n = select_menu(win, PICK_ONE, &selected);
390  | 		destroy_nhwindow(win);
391  | 
392  | 		/* Process the choice */
393  | 		if (n != 1 || selected[0].item.a_int == any.a_int)
394  | 		    goto give_up;		/* Selected quit */
395  | 
396  | 		flags.initrole = selected[0].item.a_int - 1;
397  | 		free((genericptr_t) selected),	selected = 0;
398  | 	    }
399  | 	}
400  | 
401  | 	/* Select a race, if necessary */
402  | 	/* force compatibility with role, try for compatibility with
403  | 	 * pre-selected gender/alignment */
404  | 	if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
405  | 	    /* pre-selected race not valid */
406  | 	    if (pick4u == 'y' || flags.initrace == ROLE_RANDOM) {
407  | 		flags.initrace = pick_race(flags.initrole, flags.initgend,
408  | 							flags.initalign);
409  | 		if (flags.initrace < 0) {
410  | 		    tty_putstr(BASE_WINDOW, 0, "Incompatible race!");
411  | 		    flags.initrace = randrace(flags.initrole);
412  | 		}
413  | 	    } else {	/* pick4u == 'n' */
414  | 		/* Count the number of valid races */
415  | 		n = 0;	/* number valid */
416  | 		k = 0;	/* valid race */
417  | 		for (i = 0; races[i].noun; i++) {
418  | 		    if (ok_race(flags.initrole, i, flags.initgend,
419  | 							flags.initalign)) {
420  | 			n++;
421  | 			k = i;
422  | 		    }
423  | 		}
424  | 		if (n == 0) {
425  | 		    for (i = 0; races[i].noun; i++) {
426  | 			if (validrace(flags.initrole, i)) {
427  | 			    n++;
428  | 			    k = i;
429  | 			}
430  | 		    }
431  | 		}
432  | 
433  | 		/* Permit the user to pick, if there is more than one */
434  | 		if (n > 1) {
435  | 		    win = create_nhwindow(NHW_MENU);
436  | 		    start_menu(win);
437  | 		    any.a_void = 0;         /* zero out all bits */
438  | 		    for (i = 0; races[i].noun; i++)
439  | 			if (ok_race(flags.initrole, i, flags.initgend,
440  | 							flags.initalign)) {
441  | 			    any.a_int = i+1;	/* must be non-zero */
442  | 			    add_menu(win, NO_GLYPH, &any, races[i].noun[0],
443  | 				0, ATR_NONE, races[i].noun, MENU_UNSELECTED);
444  | 			}
445  | 		    any.a_int = pick_race(flags.initrole, flags.initgend,
446  | 					flags.initalign)+1;
447  | 		    if (any.a_int == 0)	/* must be non-zero */
448  | 			any.a_int = randrace(flags.initrole)+1;
449  | 		    add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
450  | 				    "Random", MENU_UNSELECTED);
451  | 		    any.a_int = i+1;	/* must be non-zero */
452  | 		    add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
453  | 				    "Quit", MENU_UNSELECTED);
454  | 		    Sprintf(pbuf, "Pick the race of your %s",
455  | 				    roles[flags.initrole].name.m);
456  | 		    end_menu(win, pbuf);
457  | 		    n = select_menu(win, PICK_ONE, &selected);
458  | 		    destroy_nhwindow(win);
459  | 		    if (n != 1 || selected[0].item.a_int == any.a_int)
460  | 			goto give_up;		/* Selected quit */
461  | 
462  | 		    k = selected[0].item.a_int - 1;
463  | 		    free((genericptr_t) selected),	selected = 0;
464  | 		}
465  | 		flags.initrace = k;
466  | 	    }
467  | 	}
468  | 
469  | 	/* Select a gender, if necessary */
470  | 	/* force compatibility with role/race, try for compatibility with
471  | 	 * pre-selected alignment */
472  | 	if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace,
473  | 						flags.initgend)) {
474  | 	    /* pre-selected gender not valid */
475  | 	    if (pick4u == 'y' || flags.initgend == ROLE_RANDOM) {
476  | 		flags.initgend = pick_gend(flags.initrole, flags.initrace,
477  | 						flags.initalign);
478  | 		if (flags.initgend < 0) {
479  | 		    tty_putstr(BASE_WINDOW, 0, "Incompatible gender!");
480  | 		    flags.initgend = randgend(flags.initrole, flags.initrace);
481  | 		}
482  | 	    } else {	/* pick4u == 'n' */
483  | 		/* Count the number of valid genders */
484  | 		n = 0;	/* number valid */
485  | 		k = 0;	/* valid gender */
486  | 		for (i = 0; i < ROLE_GENDERS; i++) {
487  | 		    if (ok_gend(flags.initrole, flags.initrace, i,
488  | 							flags.initalign)) {
489  | 			n++;
490  | 			k = i;
491  | 		    }
492  | 		}
493  | 		if (n == 0) {
494  | 		    for (i = 0; i < ROLE_GENDERS; i++) {
495  | 			if (validgend(flags.initrole, flags.initrace, i)) {
496  | 			    n++;
497  | 			    k = i;
498  | 			}
499  | 		    }
500  | 		}
501  | 
502  | 		/* Permit the user to pick, if there is more than one */
503  | 		if (n > 1) {
504  | 		    win = create_nhwindow(NHW_MENU);
505  | 		    start_menu(win);
506  | 		    any.a_void = 0;         /* zero out all bits */
507  | 		    for (i = 0; i < ROLE_GENDERS; i++)
508  | 			if (ok_gend(flags.initrole, flags.initrace, i,
509  | 							    flags.initalign)) {
510  | 			    any.a_int = i+1;
511  | 			    add_menu(win, NO_GLYPH, &any, genders[i].adj[0],
512  | 				0, ATR_NONE, genders[i].adj, MENU_UNSELECTED);
513  | 			}
514  | 		    any.a_int = pick_gend(flags.initrole, flags.initrace,
515  | 					    flags.initalign)+1;
516  | 		    if (any.a_int == 0)	/* must be non-zero */
517  | 			any.a_int = randgend(flags.initrole, flags.initrace)+1;
518  | 		    add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
519  | 				    "Random", MENU_UNSELECTED);
520  | 		    any.a_int = i+1;	/* must be non-zero */
521  | 		    add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
522  | 				    "Quit", MENU_UNSELECTED);
523  | 		    Sprintf(pbuf, "Pick the gender of your %s %s",
524  | 				    races[flags.initrace].adj,
525  | 				    roles[flags.initrole].name.m);
526  | 		    end_menu(win, pbuf);
527  | 		    n = select_menu(win, PICK_ONE, &selected);
528  | 		    destroy_nhwindow(win);
529  | 		    if (n != 1 || selected[0].item.a_int == any.a_int)
530  | 			goto give_up;		/* Selected quit */
531  | 
532  | 		    k = selected[0].item.a_int - 1;
533  | 		    free((genericptr_t) selected),	selected = 0;
534  | 		}
535  | 		flags.initgend = k;
536  | 	    }
537  | 	}
538  | 
539  | 	/* Select an alignment, if necessary */
540  | 	/* force compatibility with role/race/gender */
541  | 	if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace,
542  | 							flags.initalign)) {
543  | 	    /* pre-selected alignment not valid */
544  | 	    if (pick4u == 'y' || flags.initalign == ROLE_RANDOM) {
545  | 		flags.initalign = pick_align(flags.initrole, flags.initrace,
546  | 							flags.initgend);
547  | 		if (flags.initalign < 0) {
548  | 		    tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!");
549  | 		    flags.initalign = randalign(flags.initrole, flags.initrace);
550  | 		}
551  | 	    } else {	/* pick4u == 'n' */
552  | 		/* Count the number of valid alignments */
553  | 		n = 0;	/* number valid */
554  | 		k = 0;	/* valid alignment */
555  | 		for (i = 0; i < ROLE_ALIGNS; i++) {
556  | 		    if (ok_align(flags.initrole, flags.initrace, flags.initgend,
557  | 							i)) {
558  | 			n++;
559  | 			k = i;
560  | 		    }
561  | 		}
562  | 		if (n == 0) {
563  | 		    for (i = 0; i < ROLE_ALIGNS; i++) {
564  | 			if (validalign(flags.initrole, flags.initrace, i)) {
565  | 			    n++;
566  | 			    k = i;
567  | 			}
568  | 		    }
569  | 		}
570  | 
571  | 		/* Permit the user to pick, if there is more than one */
572  | 		if (n > 1) {
573  | 		    win = create_nhwindow(NHW_MENU);
574  | 		    start_menu(win);
575  | 		    any.a_void = 0;         /* zero out all bits */
576  | 		    for (i = 0; i < ROLE_ALIGNS; i++)
577  | 			if (ok_align(flags.initrole, flags.initrace,
578  | 							flags.initgend, i)) {
579  | 			    any.a_int = i+1;
580  | 			    add_menu(win, NO_GLYPH, &any, aligns[i].adj[0],
581  | 				 0, ATR_NONE, aligns[i].adj, MENU_UNSELECTED);
582  | 			}
583  | 		    any.a_int = pick_align(flags.initrole, flags.initrace,
584  | 					    flags.initgend)+1;
585  | 		    if (any.a_int == 0)	/* must be non-zero */
586  | 			any.a_int = randalign(flags.initrole, flags.initrace)+1;
587  | 		    add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
588  | 				    "Random", MENU_UNSELECTED);
589  | 		    any.a_int = i+1;	/* must be non-zero */
590  | 		    add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
591  | 				    "Quit", MENU_UNSELECTED);
592  | 		    Sprintf(pbuf, "Pick the alignment of your %s %s %s",
593  | 			    genders[flags.initgend].adj,
594  | 			    races[flags.initrace].adj,
595  | 			    (flags.initgend && roles[flags.initrole].name.f) ?
596  | 			    roles[flags.initrole].name.f :
597  | 			    roles[flags.initrole].name.m);
598  | 		    end_menu(win, pbuf);
599  | 		    n = select_menu(win, PICK_ONE, &selected);
600  | 		    destroy_nhwindow(win);
601  | 		    if (n != 1 || selected[0].item.a_int == any.a_int)
602  | 			goto give_up;		/* Selected quit */
603  | 
604  | 		    k = selected[0].item.a_int - 1;
605  | 		    free((genericptr_t) selected),	selected = 0;
606  | 		}
607  | 		flags.initalign = k;
608  | 	    }
609  | 	}
610  | 	/* Success! */
611  | 	tty_display_nhwindow(BASE_WINDOW, FALSE);
612  | }
613  | 
614  | /*
615  |  * plname is filled either by an option (-u Player  or  -uPlayer) or
616  |  * explicitly (by being the wizard) or by askname.
617  |  * It may still contain a suffix denoting the role, etc.
618  |  * Always called after init_nhwindows() and before display_gamewindows().
619  |  */
620  | void
621  | tty_askname()
622  | {
623  |     static char who_are_you[] = "Who are you? ";
624  |     register int c, ct, tryct = 0;
625  | 
626  |     tty_putstr(BASE_WINDOW, 0, "");
627  |     do {
628  | 	if (++tryct > 1) {
629  | 	    if (tryct > 10) bail("Giving up after 10 tries.\n");
630  | 	    tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury - 1);
631  | 	    tty_putstr(BASE_WINDOW, 0, "Enter a name for your character...");
632  | 	    /* erase previous prompt (in case of ESC after partial response) */
633  | 	    tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury),  cl_end();
634  | 	}
635  | 	tty_putstr(BASE_WINDOW, 0, who_are_you);
636  | 	tty_curs(BASE_WINDOW, (int)(sizeof who_are_you),
637  | 		 wins[BASE_WINDOW]->cury - 1);
638  | 	ct = 0;
639  | 	while((c = tty_nhgetch()) != '\n') {
640  | 		if(c == EOF) error("End of input\n");
641  | 		if (c == '\033') { ct = 0; break; }  /* continue outer loop */
642  | 		/* some people get confused when their erase char is not ^H */
643  | 		if (c == '\b' || c == '\177') {
644  | 			if(ct) {
645  | 				ct--;
646  | #ifdef MICRO
647  | # if defined(WIN32CON)
648  | 				backsp();       /* \b is visible on NT */
649  | # else
650  | #  if defined(MSDOS)
651  | 				if (iflags.grmode) {
652  | 					backsp();
653  | 				} else
654  | 
655  | #  endif
656  | 				msmsg("\b \b");
657  | # endif
658  | #else
659  | 				(void) putchar('\b');
660  | 				(void) putchar(' ');
661  | 				(void) putchar('\b');
662  | #endif
663  | 			}
664  | 			continue;
665  | 		}
666  | #if defined(UNIX) || defined(VMS)
667  | 		if(c != '-' && c != '@')
668  | 		if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_';
669  | #endif
670  | 		if (ct < (int)(sizeof plname) - 1) {
671  | #if defined(MICRO)
672  | # if defined(MSDOS)
673  | 			if (iflags.grmode) {
674  | 				(void) putchar(c);
675  | 			} else
676  | # endif
677  | 			msmsg("%c", c);
678  | #else
679  | 			(void) putchar(c);
680  | #endif
681  | 			plname[ct++] = c;
682  | 		}
683  | 	}
684  | 	plname[ct] = 0;
685  |     } while (ct == 0);
686  | 
687  |     /* move to next line to simulate echo of user's <return> */
688  |     tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury + 1);
689  | }
690  | 
691  | void
692  | tty_get_nh_event()
693  | {
694  |     return;
695  | }
696  | 
697  | #ifndef MICRO
698  | STATIC_OVL void
699  | getret()
700  | {
701  | 	xputs("\n");
702  | 	if(flags.standout)
703  | 		standoutbeg();
704  | 	xputs("Hit ");
705  | 	xputs(iflags.cbreak ? "space" : "return");
706  | 	xputs(" to continue: ");
707  | 	if(flags.standout)
708  | 		standoutend();
709  | 	xwaitforspace(" ");
710  | }
711  | #endif
712  | 
713  | void
714  | tty_suspend_nhwindows(str)
715  |     const char *str;
716  | {
717  |     settty(str);		/* calls end_screen, perhaps raw_print */
718  |     if (!str) tty_raw_print("");	/* calls fflush(stdout) */
719  | }
720  | 
721  | void
722  | tty_resume_nhwindows()
723  | {
724  |     gettty();
725  |     setftty();			/* calls start_screen */
726  |     docrt();
727  | }
728  | 
729  | void
730  | tty_exit_nhwindows(str)
731  |     const char *str;
732  | {
733  |     winid i;
734  | 
735  |     tty_suspend_nhwindows(str);
736  |     /* Just forget any windows existed, since we're about to exit anyway.
737  |      * Disable windows to avoid calls to window routines.
738  |      */
739  |     for(i=0; i<MAXWIN; i++)
740  | 	if (wins[i] && (i != BASE_WINDOW)) {
741  | #ifdef FREE_ALL_MEMORY
742  | 	    free_window_info(wins[i], TRUE);
743  | 	    free((genericptr_t) wins[i]);
744  | #endif
745  | 	    wins[i] = 0;
746  | 	}
747  | #ifndef NO_TERMS	/*(until this gets added to the window interface)*/
748  |     tty_shutdown();		/* cleanup termcap/terminfo/whatever */
749  | #endif
750  |     iflags.window_inited = 0;
751  | }
752  | 
753  | winid
754  | tty_create_nhwindow(type)
755  |     int type;
756  | {
757  |     struct WinDesc* newwin;
758  |     int i;
759  |     int newid;
760  | 
761  |     if(maxwin == MAXWIN)
762  | 	return WIN_ERR;
763  | 
764  |     newwin = (struct WinDesc*) alloc(sizeof(struct WinDesc));
765  |     newwin->type = type;
766  |     newwin->flags = 0;
767  |     newwin->active = FALSE;
768  |     newwin->curx = newwin->cury = 0;
769  |     newwin->morestr = 0;
770  |     newwin->mlist = (tty_menu_item *) 0;
771  |     newwin->plist = (tty_menu_item **) 0;
772  |     newwin->npages = newwin->plist_size = newwin->nitems = newwin->how = 0;
773  |     switch(type) {
774  |     case NHW_BASE:
775  | 	/* base window, used for absolute movement on the screen */
776  | 	newwin->offx = newwin->offy = 0;
777  | 	newwin->rows = ttyDisplay->rows;
778  | 	newwin->cols = ttyDisplay->cols;
779  | 	newwin->maxrow = newwin->maxcol = 0;
780  | 	break;
781  |     case NHW_MESSAGE:
782  | 	/* message window, 1 line long, very wide, top of screen */
783  | 	newwin->offx = newwin->offy = 0;
784  | 	/* sanity check */
785  | 	if(iflags.msg_history < 20) iflags.msg_history = 20;
786  | 	else if(iflags.msg_history > 60) iflags.msg_history = 60;
787  | 	newwin->maxrow = newwin->rows = iflags.msg_history;
788  | 	newwin->maxcol = newwin->cols = 0;
789  | 	break;
790  |     case NHW_STATUS:
791  | 	/* status window, 2 lines long, full width, bottom of screen */
792  | 	newwin->offx = 0;
793  | #if defined(USE_TILES) && defined(MSDOS)
794  | 	if (iflags.grmode) {
795  | 		newwin->offy = ttyDisplay->rows-2;
796  | 	} else
797  | #endif
798  | 	newwin->offy = min((int)ttyDisplay->rows-2, ROWNO+1);
799  | 	newwin->rows = newwin->maxrow = 2;
800  | 	newwin->cols = newwin->maxcol = min(ttyDisplay->cols, COLNO);
801  | 	break;
802  |     case NHW_MAP:
803  | 	/* map window, ROWNO lines long, full width, below message window */
804  | 	newwin->offx = 0;
805  | 	newwin->offy = 1;
806  | 	newwin->rows = ROWNO;
807  | 	newwin->cols = COLNO;
808  | 	newwin->maxrow = 0;	/* no buffering done -- let gbuf do it */
809  | 	newwin->maxcol = 0;
810  | 	break;
811  |     case NHW_MENU:
812  |     case NHW_TEXT:
813  | 	/* inventory/menu window, variable length, full width, top of screen */
814  | 	/* help window, the same, different semantics for display, etc */
815  | 	newwin->offx = newwin->offy = 0;
816  | 	newwin->rows = 0;
817  | 	newwin->cols = ttyDisplay->cols;
818  | 	newwin->maxrow = newwin->maxcol = 0;
819  | 	break;
820  |    default:
821  | 	panic("Tried to create window type %d\n", (int) type);
822  | 	return WIN_ERR;
823  |     }
824  | 
825  |     for(newid = 0; newid<MAXWIN; newid++) {
826  | 	if(wins[newid] == 0) {
827  | 	    wins[newid] = newwin;
828  | 	    break;
829  | 	}
830  |     }
831  |     if(newid == MAXWIN) {
832  | 	panic("No window slots!");
833  | 	return WIN_ERR;
834  |     }
835  | 
836  |     if(newwin->maxrow) {
837  | 	newwin->data =
838  | 		(char **) alloc(sizeof(char *) * (unsigned)newwin->maxrow);
839  | 	newwin->datlen =
840  | 		(short *) alloc(sizeof(short) * (unsigned)newwin->maxrow);
841  | 	if(newwin->maxcol) {
842  | 	    for (i = 0; i < newwin->maxrow; i++) {
843  | 		newwin->data[i] = (char *) alloc((unsigned)newwin->maxcol);
844  | 		newwin->datlen[i] = newwin->maxcol;
845  | 	    }
846  | 	} else {
847  | 	    for (i = 0; i < newwin->maxrow; i++) {
848  | 		newwin->data[i] = (char *) 0;
849  | 		newwin->datlen[i] = 0;
850  | 	    }
851  | 	}
852  | 	if(newwin->type == NHW_MESSAGE)
853  | 	    newwin->maxrow = 0;
854  |     } else {
855  | 	newwin->data = (char **)0;
856  | 	newwin->datlen = (short *)0;
857  |     }
858  | 
859  |     return newid;
860  | }
861  | 
862  | STATIC_OVL void
863  | erase_menu_or_text(window, cw, clear)
864  |     winid window;
865  |     struct WinDesc *cw;
866  |     boolean clear;
867  | {
868  |     if(cw->offx == 0)
869  | 	if(cw->offy) {
870  | 	    tty_curs(window, 1, 0);
871  | 	    cl_eos();
872  | 	} else if (clear)
873  | 	    clear_screen();
874  | 	else
875  | 	    docrt();
876  |     else
877  | 	docorner((int)cw->offx, cw->maxrow+1);
878  | }
879  | 
880  | STATIC_OVL void
881  | free_window_info(cw, free_data)
882  |     struct WinDesc *cw;
883  |     boolean free_data;
884  | {
885  |     int i;
886  | 
887  |     if (cw->data) {
888  | 	if (cw == wins[WIN_MESSAGE] && cw->rows > cw->maxrow)
889  | 	    cw->maxrow = cw->rows;		/* topl data */
890  | 	for(i=0; i<cw->maxrow; i++)
891  | 	    if(cw->data[i]) {
892  | 		free((genericptr_t)cw->data[i]);
893  | 		cw->data[i] = (char *)0;
894  | 		if (cw->datlen) cw->datlen[i] = 0;
895  | 	    }
896  | 	if (free_data) {
897  | 	    free((genericptr_t)cw->data);
898  | 	    cw->data = (char **)0;
899  | 	    if (cw->datlen) free((genericptr_t)cw->datlen);
900  | 	    cw->datlen = (short *)0;
901  | 	    cw->rows = 0;
902  | 	}
903  |     }
904  |     cw->maxrow = cw->maxcol = 0;
905  |     if(cw->mlist) {
906  | 	tty_menu_item *temp;
907  | 	while ((temp = cw->mlist) != 0) {
908  | 	    cw->mlist = cw->mlist->next;
909  | 	    if (temp->str) free((genericptr_t)temp->str);
910  | 	    free((genericptr_t)temp);
911  | 	}
912  |     }
913  |     if (cw->plist) {
914  | 	free((genericptr_t)cw->plist);
915  | 	cw->plist = 0;
916  |     }
917  |     cw->plist_size = cw->npages = cw->nitems = cw->how = 0;
918  |     if(cw->morestr) {
919  | 	free((genericptr_t)cw->morestr);
920  | 	cw->morestr = 0;
921  |     }
922  | }
923  | 
924  | void
925  | tty_clear_nhwindow(window)
926  |     winid window;
927  | {
928  |     register struct WinDesc *cw = 0;
929  | 
930  |     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
931  | 	panic(winpanicstr,  window);
932  |     ttyDisplay->lastwin = window;
933  | 
934  |     switch(cw->type) {
935  |     case NHW_MESSAGE:
936  | 	if(ttyDisplay->toplin) {
937  | 	    home();
938  | 	    cl_end();
939  | 	    if(cw->cury)
940  | 		docorner(1, cw->cury+1);
941  | 	    ttyDisplay->toplin = 0;
942  | 	}
943  | 	break;
944  |     case NHW_STATUS:
945  | 	tty_curs(window, 1, 0);
946  | 	cl_end();
947  | 	tty_curs(window, 1, 1);
948  | 	cl_end();
949  | 	break;
950  |     case NHW_MAP:
951  | 	/* cheap -- clear the whole thing and tell nethack to redraw botl */
952  | 	flags.botlx = 1;
953  | 	/* fall into ... */
954  |     case NHW_BASE:
955  | 	clear_screen();
956  | 	break;
957  |     case NHW_MENU:
958  |     case NHW_TEXT:
959  | 	if(cw->active)
960  | 	    erase_menu_or_text(window, cw, TRUE);
961  | 	free_window_info(cw, FALSE);
962  | 	break;
963  |     }
964  |     cw->curx = cw->cury = 0;
965  | }
966  | 
967  | STATIC_OVL void
968  | dmore(cw, s)
969  |     register struct WinDesc *cw;
970  |     const char *s;			/* valid responses */
971  | {
972  |     const char *prompt = cw->morestr ? cw->morestr : defmorestr;
973  |     int offset = (cw->type == NHW_TEXT) ? 1 : 2;
974  | 
975  |     tty_curs(BASE_WINDOW,
976  | 	     (int)ttyDisplay->curx + offset, (int)ttyDisplay->cury);
977  |     if(flags.standout)
978  | 	standoutbeg();
979  |     xputs(prompt);
980  |     ttyDisplay->curx += strlen(prompt);
981  |     if(flags.standout)
982  | 	standoutend();
983  | 
984  |     xwaitforspace(s);
985  | }
986  | 
987  | STATIC_OVL void
988  | set_item_state(window, lineno, item)
989  |     winid window;
990  |     int lineno;
991  |     tty_menu_item *item;
992  | {
993  |     char ch = item->selected ? (item->count == -1L ? '+' : '#') : '-';
994  |     tty_curs(window, 4, lineno);
995  |     term_start_attr(item->attr);
996  |     (void) putchar(ch);
997  |     ttyDisplay->curx++;
998  |     term_end_attr(item->attr);
999  | }
1000 | 
1001 | STATIC_OVL void
1002 | set_all_on_page(window, page_start, page_end)
1003 |     winid window;
1004 |     tty_menu_item *page_start, *page_end;
1005 | {
1006 |     tty_menu_item *curr;
1007 |     int n;
1008 | 
1009 |     for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1010 | 	if (curr->identifier.a_void && !curr->selected) {
1011 | 	    curr->selected = TRUE;
1012 | 	    set_item_state(window, n, curr);
1013 | 	}
1014 | }
1015 | 
1016 | STATIC_OVL void
1017 | unset_all_on_page(window, page_start, page_end)
1018 |     winid window;
1019 |     tty_menu_item *page_start, *page_end;
1020 | {
1021 |     tty_menu_item *curr;
1022 |     int n;
1023 | 
1024 |     for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1025 | 	if (curr->identifier.a_void && curr->selected) {
1026 | 	    curr->selected = FALSE;
1027 | 	    curr->count = -1L;
1028 | 	    set_item_state(window, n, curr);
1029 | 	}
1030 | }
1031 | 
1032 | STATIC_OVL void
1033 | invert_all_on_page(window, page_start, page_end, acc)
1034 |     winid window;
1035 |     tty_menu_item *page_start, *page_end;
1036 |     char acc;	/* group accelerator, 0 => all */
1037 | {
1038 |     tty_menu_item *curr;
1039 |     int n;
1040 | 
1041 |     for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1042 | 	if (curr->identifier.a_void && (acc == 0 || curr->gselector == acc)) {
1043 | 	    if (curr->selected) {
1044 | 		curr->selected = FALSE;
1045 | 		curr->count = -1L;
1046 | 	    } else
1047 | 		curr->selected = TRUE;
1048 | 	    set_item_state(window, n, curr);
1049 | 	}
1050 | }
1051 | 
1052 | /*
1053 |  * Invert all entries that match the give group accelerator (or all if
1054 |  * zero).
1055 |  */
1056 | STATIC_OVL void
1057 | invert_all(window, page_start, page_end, acc)
1058 |     winid window;
1059 |     tty_menu_item *page_start, *page_end;
1060 |     char acc;	/* group accelerator, 0 => all */
1061 | {
1062 |     tty_menu_item *curr;
1063 |     boolean on_curr_page;
1064 |     struct WinDesc *cw =  wins[window];
1065 | 
1066 |     invert_all_on_page(window, page_start, page_end, acc);
1067 | 
1068 |     /* invert the rest */
1069 |     for (on_curr_page = FALSE, curr = cw->mlist; curr; curr = curr->next) {
1070 | 	if (curr == page_start)
1071 | 	    on_curr_page = TRUE;
1072 | 	else if (curr == page_end)
1073 | 	    on_curr_page = FALSE;
1074 | 
1075 | 	if (!on_curr_page && curr->identifier.a_void
1076 | 				&& (acc == 0 || curr->gselector == acc)) {
1077 | 	    if (curr->selected) {
1078 | 		curr->selected = FALSE;
1079 | 		curr->count = -1;
1080 | 	    } else
1081 | 		curr->selected = TRUE;
1082 | 	}
1083 |     }
1084 | }
1085 | 
1086 | STATIC_OVL void
1087 | process_menu_window(window, cw)
1088 | winid window;
1089 | struct WinDesc *cw;
1090 | {
1091 |     tty_menu_item *page_start, *page_end, *curr;
1092 |     long count;
1093 |     int n, curr_page, page_lines;
1094 |     boolean finished, counting, reset_count;
1095 |     char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ],
1096 | 	 *msave, morestr[QBUFSZ];
1097 | 
1098 |     curr_page = page_lines = 0;
1099 |     page_start = page_end = 0;
1100 |     msave = cw->morestr;	/* save the morestr */
1101 |     cw->morestr = morestr;
1102 |     counting = FALSE;
1103 |     count = 0L;
1104 |     reset_count = TRUE;
1105 |     finished = FALSE;
1106 | 
1107 |     /* collect group accelerators; for PICK_NONE, they're ignored;
1108 |        for PICK_ONE, only those which match exactly one entry will be
1109 |        accepted; for PICK_ANY, those which match any entry are okay */
1110 |     gacc[0] = '\0';
1111 |     if (cw->how != PICK_NONE) {
1112 | 	int i, gcnt[128];
1113 | #define GSELIDX(c) (c & 127)	/* guard against `signed char' */
1114 | 
1115 | 	for (i = 0; i < SIZE(gcnt); i++) gcnt[i] = 0;
1116 | 	for (n = 0, curr = cw->mlist; curr; curr = curr->next)
1117 | 	    if (curr->gselector) ++n,  ++gcnt[GSELIDX(curr->gselector)];
1118 | 
1119 | 	if (n > 0)	/* at least one group accelerator found */
1120 | 	    for (rp = gacc, curr = cw->mlist; curr; curr = curr->next)
1121 | 		if (curr->gselector && !index(gacc, curr->gselector) &&
1122 | 			(cw->how == PICK_ANY ||
1123 | 			    gcnt[GSELIDX(curr->gselector)] == 1)) {
1124 | 		    *rp++ = curr->gselector;
1125 | 		    *rp = '\0';	/* re-terminate for index() */
1126 | 		}
1127 |     }
1128 | 
1129 |     /* loop until finished */
1130 |     while (!finished) {
1131 | 	if (reset_count) {
1132 | 	    counting = FALSE;
1133 | 	    count = 0;
1134 | 	} else
1135 | 	    reset_count = TRUE;
1136 | 
1137 | 	if (!page_start) {
1138 | 	    /* new page to be displayed */
1139 | 	    if (curr_page < 0 || (cw->npages > 0 && curr_page >= cw->npages))
1140 | 		panic("bad menu screen page #%d", curr_page);
1141 | 
1142 | 	    /* clear screen */
1143 | 	    if (!cw->offx) {	/* if not corner, do clearscreen */
1144 | 		if(cw->offy) {
1145 | 		    tty_curs(window, 1, 0);
1146 | 		    cl_eos();
1147 | 		} else
1148 | 		    clear_screen();
1149 | 	    }
1150 | 
1151 | 	    rp = resp;
1152 | 	    if (cw->npages > 0) {
1153 | 		/* collect accelerators */
1154 | 		page_start = cw->plist[curr_page];
1155 | 		page_end = cw->plist[curr_page + 1];
1156 | 		for (page_lines = 0, curr = page_start;
1157 | 			curr != page_end;
1158 | 			page_lines++, curr = curr->next) {
1159 | 		    if (curr->selector)
1160 | 			*rp++ = curr->selector;
1161 | 
1162 | 		    tty_curs(window, 1, page_lines);
1163 | 		    if (cw->offx) cl_end();
1164 | 
1165 | 		    (void) putchar(' ');
1166 | 		    ++ttyDisplay->curx;
1167 | 		    /*
1168 | 		     * Don't use xputs() because (1) under unix it calls
1169 | 		     * tputstr() which will interpret a '*' as some kind
1170 | 		     * of padding information and (2) it calls xputc to
1171 | 		     * actually output the character.  We're faster doing
1172 | 		     * this.
1173 | 		     */
1174 | 		    term_start_attr(curr->attr);
1175 | 		    for (n = 0, cp = curr->str;
1176 | 			  *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
1177 | 			  cp++, n++)
1178 | 			if (n == 2 && curr->identifier.a_void != 0 &&
1179 | 							curr->selected) {
1180 | 			    if (curr->count == -1L)
1181 | 				(void) putchar('+'); /* all selected */
1182 | 			    else
1183 | 				(void) putchar('#'); /* count selected */
1184 | 			} else
1185 | 			    (void) putchar(*cp);
1186 | 		    term_end_attr(curr->attr);
1187 | 		}
1188 | 	    } else {
1189 | 		page_start = 0;
1190 | 		page_end = 0;
1191 | 		page_lines = 0;
1192 | 	    }
1193 | 	    *rp = 0;
1194 | 
1195 | 	    /* corner window - clear extra lines from last page */
1196 | 	    if (cw->offx) {
1197 | 		for (n = page_lines + 1; n < cw->maxrow; n++) {
1198 | 		    tty_curs(window, 1, n);
1199 | 		    cl_end();
1200 | 		}
1201 | 	    }
1202 | 
1203 | 	    /* set extra chars.. */
1204 | 	    Strcat(resp, default_menu_cmds);
1205 | 	    Strcat(resp, "0123456789\033\n\r");	/* counts, quit */
1206 | 	    Strcat(resp, gacc);			/* group accelerators */
1207 | 	    Strcat(resp, mapped_menu_cmds);
1208 | 
1209 | 	    if (cw->npages > 1)
1210 | 		Sprintf(cw->morestr, "(%d of %d)",
1211 | 			curr_page + 1, (int) cw->npages);
1212 | 	    else
1213 | 		Strcpy(cw->morestr, msave);
1214 | 
1215 | 	    tty_curs(window, 1, page_lines);
1216 | 	    cl_end();
1217 | 	    dmore(cw, resp);
1218 | 	} else {
1219 | 	    /* just put the cursor back... */
1220 | 	    tty_curs(window, (int) strlen(cw->morestr) + 2, page_lines);
1221 | 	    xwaitforspace(resp);
1222 | 	}
1223 | 
1224 | 	morc = map_menu_cmd(morc);
1225 | 	switch (morc) {
1226 | 	    case '0':
1227 | 		/* special case: '0' is also the default ball class */
1228 | 		if (!counting && index(gacc, morc)) goto group_accel;
1229 | 		/* fall through to count the zero */
1230 | 	    case '1': case '2': case '3': case '4':
1231 | 	    case '5': case '6': case '7': case '8': case '9':
1232 | 		count = (count * 10L) + (long) (morc - '0');
1233 | 		/*
1234 | 		 * It is debatable whether we should allow 0 to
1235 | 		 * start a count.  There is no difference if the
1236 | 		 * item is selected.  If not selected, then
1237 | 		 * "0b" could mean:
1238 | 		 *
1239 | 		 *	count starting zero:	"zero b's"
1240 | 		 *	ignore starting zero:	"select b"
1241 | 		 *
1242 | 		 * At present I don't know which is better.
1243 | 		 */
1244 | 		if (count != 0L) {	/* ignore leading zeros */
1245 | 		    counting = TRUE;
1246 | 		    reset_count = FALSE;
1247 | 		}
1248 | 		break;
1249 | 	    case '\033':	/* cancel - from counting or loop */
1250 | 		if (!counting) {
1251 | 		    /* deselect everything */
1252 | 		    for (curr = cw->mlist; curr; curr = curr->next) {
1253 | 			curr->selected = FALSE;
1254 | 			curr->count = -1L;
1255 | 		    }
1256 | 		    cw->flags |= WIN_CANCELLED;
1257 | 		    finished = TRUE;
1258 | 		}
1259 | 		/* else only stop count */
1260 | 		break;
1261 | 	    case '\0':		/* finished (commit) */
1262 | 	    case '\n':
1263 | 	    case '\r':
1264 | 		/* only finished if we are actually picking something */
1265 | 		if (cw->how != PICK_NONE) {
1266 | 		    finished = TRUE;
1267 | 		    break;
1268 | 		}
1269 | 		/* else fall through */
1270 | 	    case MENU_NEXT_PAGE:
1271 | 		if (cw->npages > 0 && curr_page != cw->npages - 1) {
1272 | 		    curr_page++;
1273 | 		    page_start = 0;
1274 | 		} else
1275 | 		    finished = TRUE;	/* questionable behavior */
1276 | 		break;
1277 | 	    case MENU_PREVIOUS_PAGE:
1278 | 		if (cw->npages > 0 && curr_page != 0) {
1279 | 		    --curr_page;
1280 | 		    page_start = 0;
1281 | 		}
1282 | 		break;
1283 | 	    case MENU_FIRST_PAGE:
1284 | 		if (cw->npages > 0 && curr_page != 0) {
1285 | 		    page_start = 0;
1286 | 		    curr_page = 0;
1287 | 		}
1288 | 		break;
1289 | 	    case MENU_LAST_PAGE:
1290 | 		if (cw->npages > 0 && curr_page != cw->npages - 1) {
1291 | 		    page_start = 0;
1292 | 		    curr_page = cw->npages - 1;
1293 | 		}
1294 | 		break;
1295 | 	    case MENU_SELECT_PAGE:
1296 | 		if (cw->how == PICK_ANY)
1297 | 		    set_all_on_page(window, page_start, page_end);
1298 | 		break;
1299 | 	    case MENU_UNSELECT_PAGE:
1300 | 		unset_all_on_page(window, page_start, page_end);
1301 | 		break;
1302 | 	    case MENU_INVERT_PAGE:
1303 | 		if (cw->how == PICK_ANY)
1304 | 		    invert_all_on_page(window, page_start, page_end, 0);
1305 | 		break;
1306 | 	    case MENU_SELECT_ALL:
1307 | 		if (cw->how == PICK_ANY) {
1308 | 		    set_all_on_page(window, page_start, page_end);
1309 | 		    /* set the rest */
1310 | 		    for (curr = cw->mlist; curr; curr = curr->next)
1311 | 			if (curr->identifier.a_void && !curr->selected)
1312 | 			    curr->selected = TRUE;
1313 | 		}
1314 | 		break;
1315 | 	    case MENU_UNSELECT_ALL:
1316 | 		unset_all_on_page(window, page_start, page_end);
1317 | 		/* unset the rest */
1318 | 		for (curr = cw->mlist; curr; curr = curr->next)
1319 | 		    if (curr->identifier.a_void && curr->selected) {
1320 | 			curr->selected = FALSE;
1321 | 			curr->count = -1;
1322 | 		    }
1323 | 		break;
1324 | 	    case MENU_INVERT_ALL:
1325 | 		if (cw->how == PICK_ANY)
1326 | 		    invert_all(window, page_start, page_end, 0);
1327 | 		break;
1328 | 	    default:
1329 | 		if (cw->how == PICK_NONE || !index(resp, morc)) {
1330 | 		    /* unacceptable input received */
1331 | 		    tty_nhbell();
1332 | 		    break;
1333 | 		} else if (index(gacc, morc)) {
1334 |  group_accel:
1335 | 		    /* group accelerator; for the PICK_ONE case, we know that
1336 | 		       it matches exactly one item in order to be in gacc[] */
1337 | 		    invert_all(window, page_start, page_end, morc);
1338 | 		    if (cw->how == PICK_ONE) finished = TRUE;
1339 | 		    break;
1340 | 		}
1341 | 		/* find, toggle, and possibly update */
1342 | 		for (n = 0, curr = page_start;
1343 | 			curr != page_end;
1344 | 			n++, curr = curr->next)
1345 | 		    if (morc == curr->selector) {
1346 | 			if (curr->selected) {
1347 | 			    if (counting && count > 0) {
1348 | 				curr->count = count;
1349 | 				set_item_state(window, n, curr);
1350 | 			    } else { /* change state */
1351 | 				curr->selected = FALSE;
1352 | 				curr->count = -1L;
1353 | 				set_item_state(window, n, curr);
1354 | 			    }
1355 | 			} else {	/* !selected */
1356 | 			    if (counting && count > 0) {
1357 | 				curr->count = count;
1358 | 				curr->selected = TRUE;
1359 | 				set_item_state(window, n, curr);
1360 | 			    } else if (!counting) {
1361 | 				curr->selected = TRUE;
1362 | 				set_item_state(window, n, curr);
1363 | 			    }
1364 | 			    /* do nothing counting&&count==0 */
1365 | 			}
1366 | 
1367 | 			if (cw->how == PICK_ONE) finished = TRUE;
1368 | 			break;	/* from `for' loop */
1369 | 		    }
1370 | 		break;
1371 | 	}
1372 | 
1373 |     } /* while */
1374 |     cw->morestr = msave;
1375 | }
1376 | 
1377 | STATIC_OVL void
1378 | process_text_window(window, cw)
1379 | winid window;
1380 | struct WinDesc *cw;
1381 | {
1382 |     int i, n, attr;
1383 |     register char *cp;
1384 | 
1385 |     for (n = 0, i = 0; i < cw->maxrow; i++) {
1386 | 	if (!cw->offx && (n + cw->offy == ttyDisplay->rows - 1)) {
1387 | 	    tty_curs(window, 1, n);
1388 | 	    cl_end();
1389 | 	    dmore(cw, quitchars);
1390 | 	    if (morc == '\033') {
1391 | 		cw->flags |= WIN_CANCELLED;
1392 | 		break;
1393 | 	    }
1394 | 	    if (cw->offy) {
1395 | 		tty_curs(window, 1, 0);
1396 | 		cl_eos();
1397 | 	    } else
1398 | 		clear_screen();
1399 | 	    n = 0;
1400 | 	}
1401 | 	tty_curs(window, 1, n++);
1402 | 	if (cw->offx) cl_end();
1403 | 	if (cw->data[i]) {
1404 | 	    attr = cw->data[i][0] - 1;
1405 | 	    if (cw->offx) {
1406 | 		(void) putchar(' '); ++ttyDisplay->curx;
1407 | 	    }
1408 | 	    term_start_attr(attr);
1409 | 	    for (cp = &cw->data[i][1];
1410 | 		    *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
1411 | 		    cp++)
1412 | 		(void) putchar(*cp);
1413 | 	    term_end_attr(attr);
1414 | 	}
1415 |     }
1416 |     if (i == cw->maxrow) {
1417 | 	tty_curs(BASE_WINDOW, (int)cw->offx + 1,
1418 | 		 (cw->type == NHW_TEXT) ? (int) ttyDisplay->rows - 1 : n);
1419 | 	cl_end();
1420 | 	dmore(cw, quitchars);
1421 | 	if (morc == '\033')
1422 | 	    cw->flags |= WIN_CANCELLED;
1423 |     }
1424 | }
1425 | 
1426 | /*ARGSUSED*/
1427 | void
1428 | tty_display_nhwindow(window, blocking)
1429 |     winid window;
1430 |     boolean blocking;	/* with ttys, all windows are blocking */
1431 | {
1432 |     register struct WinDesc *cw = 0;
1433 | 
1434 |     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1435 | 	panic(winpanicstr,  window);
1436 |     if(cw->flags & WIN_CANCELLED)
1437 | 	return;
1438 |     ttyDisplay->lastwin = window;
1439 |     ttyDisplay->rawprint = 0;
1440 | 
1441 |     switch(cw->type) {
1442 |     case NHW_MESSAGE:
1443 | 	if(ttyDisplay->toplin == 1) {
1444 | 	    more();
1445 | 	    ttyDisplay->toplin = 1; /* more resets this */
1446 | 	    tty_clear_nhwindow(window);
1447 | 	} else
1448 | 	    ttyDisplay->toplin = 0;
1449 | 	cw->curx = cw->cury = 0;
1450 | 	if(!cw->active)
1451 | 	    iflags.window_inited = TRUE;
1452 | 	break;
1453 |     case NHW_MAP:
1454 | 	end_glyphout();
1455 | 	if(blocking) {
1456 | 	    if(!ttyDisplay->toplin) ttyDisplay->toplin = 1;
1457 | 	    tty_display_nhwindow(WIN_MESSAGE, TRUE);
1458 | 	    return;
1459 | 	}
1460 |     case NHW_BASE:
1461 | 	(void) fflush(stdout);
1462 | 	break;
1463 |     case NHW_TEXT:
1464 | 	cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
1465 | 	/*FALLTHRU*/
1466 |     case NHW_MENU:
1467 | 	cw->active = 1;
1468 | 	/* avoid converting to uchar before calculations are finished */
1469 | 	cw->offx = (uchar) (int)
1470 | 	    max((int) 10, (int) (ttyDisplay->cols - cw->maxcol - 1));
1471 | 	if(cw->type == NHW_MENU)
1472 | 	    cw->offy = 0;
1473 | 	if(ttyDisplay->toplin == 1)
1474 | 	    tty_display_nhwindow(WIN_MESSAGE, TRUE);
1475 | 	if(cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows) {
1476 | 	    cw->offx = 0;
1477 | 	    if(cw->offy) {
1478 | 		tty_curs(window, 1, 0);
1479 | 		cl_eos();
1480 | 	    } else
1481 | 		clear_screen();
1482 | 	    ttyDisplay->toplin = 0;
1483 | 	} else
1484 | 	    tty_clear_nhwindow(WIN_MESSAGE);
1485 | 
1486 | 	if (cw->data)
1487 | 	    process_text_window(window, cw);
1488 | 	else
1489 | 	    process_menu_window(window, cw);
1490 | 	break;
1491 |     }
1492 |     cw->active = 1;
1493 | }
1494 | 
1495 | void
1496 | tty_dismiss_nhwindow(window)
1497 |     winid window;
1498 | {
1499 |     register struct WinDesc *cw = 0;
1500 | 
1501 |     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1502 | 	panic(winpanicstr,  window);
1503 | 
1504 |     switch(cw->type) {
1505 |     case NHW_MESSAGE:
1506 | 	if (ttyDisplay->toplin)
1507 | 	    tty_display_nhwindow(WIN_MESSAGE, TRUE);
1508 | 	/*FALLTHRU*/
1509 |     case NHW_STATUS:
1510 |     case NHW_BASE:
1511 |     case NHW_MAP:
1512 | 	/*
1513 | 	 * these should only get dismissed when the game is going away
1514 | 	 * or suspending
1515 | 	 */
1516 | 	tty_curs(BASE_WINDOW, 1, (int)ttyDisplay->rows-1);
1517 | 	cw->active = 0;
1518 | 	break;
1519 |     case NHW_MENU:
1520 |     case NHW_TEXT:
1521 | 	if(cw->active) {
1522 | 	    if (iflags.window_inited) {
1523 | 		/* otherwise dismissing the text endwin after other windows
1524 | 		 * are dismissed tries to redraw the map and panics.  since
1525 | 		 * the whole reason for dismissing the other windows was to
1526 | 		 * leave the ending window on the screen, we don't want to
1527 | 		 * erase it anyway.
1528 | 		 */
1529 | 		erase_menu_or_text(window, cw, FALSE);
1530 | 	    }
1531 | 	    cw->active = 0;
1532 | 	}
1533 | 	break;
1534 |     }
1535 |     cw->flags = 0;
1536 | }
1537 | 
1538 | void
1539 | tty_destroy_nhwindow(window)
1540 |     winid window;
1541 | {
1542 |     register struct WinDesc *cw = 0;
1543 | 
1544 |     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1545 | 	panic(winpanicstr,  window);
1546 | 
1547 |     if(cw->active)
1548 | 	tty_dismiss_nhwindow(window);
1549 |     if(cw->type == NHW_MESSAGE)
1550 | 	iflags.window_inited = 0;
1551 |     if(cw->type == NHW_MAP)
1552 | 	clear_screen();
1553 | 
1554 |     free_window_info(cw, TRUE);
1555 |     free((genericptr_t)cw);
1556 |     wins[window] = 0;
1557 | }
1558 | 
1559 | void
1560 | tty_curs(window, x, y)
1561 | winid window;
1562 | register int x, y;	/* not xchar: perhaps xchar is unsigned and
1563 | 			   curx-x would be unsigned as well */
1564 | {
1565 |     struct WinDesc *cw = 0;
1566 |     int cx = ttyDisplay->curx;
1567 |     int cy = ttyDisplay->cury;
1568 | 
1569 |     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1570 | 	panic(winpanicstr,  window);
1571 |     ttyDisplay->lastwin = window;
1572 | 
1573 | #if defined(USE_TILES) && defined(MSDOS)
1574 |     adjust_cursor_flags(cw);
1575 | #endif
1576 |     cw->curx = --x;	/* column 0 is never used */
1577 |     cw->cury = y;
1578 | #ifdef DEBUG
1579 |     if(x<0 || y<0 || y >= cw->rows || x >= cw->cols) {
1580 | 	const char *s = "[unknown type]";
1581 | 	switch(cw->type) {
1582 | 	case NHW_MESSAGE: s = "[topl window]"; break;
1583 | 	case NHW_STATUS: s = "[status window]"; break;
1584 | 	case NHW_MAP: s = "[map window]"; break;
1585 | 	case NHW_MENU: s = "[corner window]"; break;
1586 | 	case NHW_TEXT: s = "[text window]"; break;
1587 | 	case NHW_BASE: s = "[base window]"; break;
1588 | 	}
1589 | 	impossible("bad curs positioning win %d %s (%d,%d)", window, s, x, y);
1590 | 	return;
1591 |     }
1592 | #endif
1593 |     x += cw->offx;
1594 |     y += cw->offy;
1595 | 
1596 | #ifdef CLIPPING
1597 |     if(clipping && window == WIN_MAP) {
1598 | 	x -= clipx;
1599 | 	y -= clipy;
1600 |     }
1601 | #endif
1602 | 
1603 |     if (y == cy && x == cx)
1604 | 	return;
1605 | 
1606 |     if(cw->type == NHW_MAP)
1607 | 	end_glyphout();
1608 | 
1609 | #ifndef NO_TERMS
1610 |     if(!nh_ND && (cx != x || x <= 3)) { /* Extremely primitive */
1611 | 	cmov(x, y); /* bunker!wtm */
1612 | 	return;
1613 |     }
1614 | #endif
1615 | 
1616 |     if((cy -= y) < 0) cy = -cy;
1617 |     if((cx -= x) < 0) cx = -cx;
1618 |     if(cy <= 3 && cx <= 3) {
1619 | 	nocmov(x, y);
1620 | #ifndef NO_TERMS
1621 |     } else if ((x <= 3 && cy <= 3) || (!nh_CM && x < cx)) {
1622 | 	(void) putchar('\r');
1623 | 	ttyDisplay->curx = 0;
1624 | 	nocmov(x, y);
1625 |     } else if (!nh_CM) {
1626 | 	nocmov(x, y);
1627 | #endif
1628 |     } else
1629 | 	cmov(x, y);
1630 | 
1631 |     ttyDisplay->curx = x;
1632 |     ttyDisplay->cury = y;
1633 | }
1634 | 
1635 | STATIC_OVL void
1636 | tty_putsym(window, x, y, ch)
1637 |     winid window;
1638 |     int x, y;
1639 |     char ch;
1640 | {
1641 |     register struct WinDesc *cw = 0;
1642 | 
1643 |     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1644 | 	panic(winpanicstr,  window);
1645 | 
1646 |     switch(cw->type) {
1647 |     case NHW_STATUS:
1648 |     case NHW_MAP:
1649 |     case NHW_BASE:
1650 | 	tty_curs(window, x, y);
1651 | 	(void) putchar(ch);
1652 | 	ttyDisplay->curx++;
1653 | 	cw->curx++;
1654 | 	break;
1655 |     case NHW_MESSAGE:
1656 |     case NHW_MENU:
1657 |     case NHW_TEXT:
1658 | 	impossible("Can't putsym to window type %d", cw->type);
1659 | 	break;
1660 |     }
1661 | }
1662 | 
1663 | 
1664 | STATIC_OVL const char*
1665 | compress_str(str)
1666 | const char *str;
1667 | {
1668 | 	static char cbuf[BUFSZ];
1669 | 	/* compress in case line too long */
1670 | 	if((int)strlen(str) >= CO) {
1671 | 		register const char *bp0 = str;
1672 | 		register char *bp1 = cbuf;
1673 | 
1674 | 		do {
1675 | #ifdef CLIPPING
1676 | 			if(*bp0 != ' ' || bp0[1] != ' ')
1677 | #else
1678 | 			if(*bp0 != ' ' || bp0[1] != ' ' || bp0[2] != ' ')
1679 | #endif
1680 | 				*bp1++ = *bp0;
1681 | 		} while(*bp0++);
1682 | 	} else
1683 | 	    return str;
1684 | 	return cbuf;
1685 | }
1686 | 
1687 | void
1688 | tty_putstr(window, attr, str)
1689 |     winid window;
1690 |     int attr;
1691 |     const char *str;
1692 | {
1693 |     register struct WinDesc *cw = 0;
1694 |     register char *ob;
1695 |     register const char *nb;
1696 |     register int i, j, n0;
1697 | 
1698 |     /* Assume there's a real problem if the window is missing --
1699 |      * probably a panic message
1700 |      */
1701 |     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) {
1702 | 	tty_raw_print(str);
1703 | 	return;
1704 |     }
1705 | 
1706 |     if(str == (const char*)0 || (cw->flags & WIN_CANCELLED))
1707 | 	return;
1708 |     if(cw->type != NHW_MESSAGE)
1709 | 	str = compress_str(str);
1710 | 
1711 |     ttyDisplay->lastwin = window;
1712 | 
1713 |     switch(cw->type) {
1714 |     case NHW_MESSAGE:
1715 | 	/* really do this later */
1716 | 	update_topl(str);
1717 | 	break;
1718 | 
1719 |     case NHW_STATUS:
1720 | 	ob = &cw->data[cw->cury][j = cw->curx];
1721 | 	if(flags.botlx) *ob = 0;
1722 | 	if(!cw->cury && (int)strlen(str) >= CO) {
1723 | 	    /* the characters before "St:" are unnecessary */
1724 | 	    nb = index(str, ':');
1725 | 	    if(nb && nb > str+2)
1726 | 		str = nb - 2;
1727 | 	}
1728 | 	nb = str;
1729 | 	for(i = cw->curx+1, n0 = cw->cols; i < n0; i++, nb++) {
1730 | 	    if(!*nb) {
1731 | 		if(*ob || flags.botlx) {
1732 | 		    /* last char printed may be in middle of line */
1733 | 		    tty_curs(WIN_STATUS, i, cw->cury);
1734 | 		    cl_end();
1735 | 		}
1736 | 		break;
1737 | 	    }
1738 | 	    if(*ob != *nb)
1739 | 		tty_putsym(WIN_STATUS, i, cw->cury, *nb);
1740 | 	    if(*ob) ob++;
1741 | 	}
1742 | 
1743 | 	(void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1);
1744 | 	cw->data[cw->cury][cw->cols-1] = '\0'; /* null terminate */
1745 | 	cw->cury = (cw->cury+1) % 2;
1746 | 	cw->curx = 0;
1747 | 	break;
1748 |     case NHW_MAP:
1749 | 	tty_curs(window, cw->curx+1, cw->cury);
1750 | 	term_start_attr(attr);
1751 | 	while(*str && (int) ttyDisplay->curx < (int) ttyDisplay->cols-1) {
1752 | 	    (void) putchar(*str);
1753 | 	    str++;
1754 | 	    ttyDisplay->curx++;
1755 | 	}
1756 | 	cw->curx = 0;
1757 | 	cw->cury++;
1758 | 	term_end_attr(attr);
1759 | 	break;
1760 |     case NHW_BASE:
1761 | 	tty_curs(window, cw->curx+1, cw->cury);
1762 | 	term_start_attr(attr);
1763 | 	while (*str) {
1764 | 	    if ((int) ttyDisplay->curx >= (int) ttyDisplay->cols-1) {
1765 | 		cw->curx = 0;
1766 | 		cw->cury++;
1767 | 		tty_curs(window, cw->curx+1, cw->cury);
1768 | 	    }
1769 | 	    (void) putchar(*str);
1770 | 	    str++;
1771 | 	    ttyDisplay->curx++;
1772 | 	}
1773 | 	cw->curx = 0;
1774 | 	cw->cury++;
1775 | 	term_end_attr(attr);
1776 | 	break;
1777 |     case NHW_MENU:
1778 |     case NHW_TEXT:
1779 | 	if(cw->type == NHW_TEXT && cw->cury == ttyDisplay->rows-1) {
1780 | 	    /* not a menu, so save memory and output 1 page at a time */
1781 | 	    cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
1782 | 	    tty_display_nhwindow(window, TRUE);
1783 | 	    for(i=0; i<cw->maxrow; i++)
1784 | 		if(cw->data[i]){
1785 | 		    free((genericptr_t)cw->data[i]);
1786 | 		    cw->data[i] = 0;
1787 | 		}
1788 | 	    cw->maxrow = cw->cury = 0;
1789 | 	}
1790 | 	/* always grows one at a time, but alloc 12 at a time */
1791 | 	if(cw->cury >= cw->rows) {
1792 | 	    char **tmp;
1793 | 
1794 | 	    cw->rows += 12;
1795 | 	    tmp = (char **) alloc(sizeof(char *) * (unsigned)cw->rows);
1796 | 	    for(i=0; i<cw->maxrow; i++)
1797 | 		tmp[i] = cw->data[i];
1798 | 	    if(cw->data)
1799 | 		free((genericptr_t)cw->data);
1800 | 	    cw->data = tmp;
1801 | 
1802 | 	    for(i=cw->maxrow; i<cw->rows; i++)
1803 | 		cw->data[i] = 0;
1804 | 	}
1805 | 	if(cw->data[cw->cury])
1806 | 	    free((genericptr_t)cw->data[cw->cury]);
1807 | 	n0 = strlen(str) + 1;
1808 | 	ob = cw->data[cw->cury] = (char *)alloc((unsigned)n0 + 1);
1809 | 	*ob++ = (char)(attr + 1);	/* avoid nuls, for convenience */
1810 | 	Strcpy(ob, str);
1811 | 
1812 | 	if(n0 > cw->maxcol)
1813 | 	    cw->maxcol = n0;
1814 | 	if(++cw->cury > cw->maxrow)
1815 | 	    cw->maxrow = cw->cury;
1816 | 	if(n0 > CO) {
1817 | 	    /* attempt to break the line */
1818 | 	    for(i = CO-1; i && str[i] != ' ';)
1819 | 		i--;
1820 | 	    if(i) {
1821 | 		cw->data[cw->cury-1][++i] = '\0';
1822 | 		tty_putstr(window, attr, &str[i]);
1823 | 	    }
1824 | 
1825 | 	}
1826 | 	break;
1827 |     }
1828 | }
1829 | 
1830 | void
1831 | tty_display_file(fname, complain)
1832 | const char *fname;
1833 | boolean complain;
1834 | {
1835 | #ifdef DEF_PAGER			/* this implies that UNIX is defined */
1836 |     {
1837 | 	/* use external pager; this may give security problems */
1838 | 	register int fd = open(fname, 0);
1839 | 
1840 | 	if(fd < 0) {
1841 | 	    if(complain) pline("Cannot open %s.", fname);
1842 | 	    else docrt();
1843 | 	    return;
1844 | 	}
1845 | 	if(child(1)) {
1846 | 	    /* Now that child() does a setuid(getuid()) and a chdir(),
1847 | 	       we may not be able to open file fname anymore, so make
1848 | 	       it stdin. */
1849 | 	    (void) close(0);
1850 | 	    if(dup(fd)) {
1851 | 		if(complain) raw_printf("Cannot open %s as stdin.", fname);
1852 | 	    } else {
1853 | 		(void) execlp(catmore, "page", (char *)0);
1854 | 		if(complain) raw_printf("Cannot exec %s.", catmore);
1855 | 	    }
1856 | 	    if(complain) sleep(10); /* want to wait_synch() but stdin is gone */
1857 | 	    terminate(EXIT_FAILURE);
1858 | 	}
1859 | 	(void) close(fd);
1860 |     }
1861 | #else	/* DEF_PAGER */
1862 |     {
1863 | 	dlb *f;
1864 | 	char buf[BUFSZ];
1865 | 	char *cr;
1866 | 
1867 | 	tty_clear_nhwindow(WIN_MESSAGE);
1868 | 	f = dlb_fopen(fname, "r");
1869 | 	if (!f) {
1870 | 	    if(complain) {
1871 | 		home();  tty_mark_synch();  tty_raw_print("");
1872 | 		perror(fname);  tty_wait_synch();
1873 | 		pline("Cannot open \"%s\".", fname);
1874 | 	    } else if(u.ux) docrt();
1875 | 	} else {
1876 | 	    winid datawin = tty_create_nhwindow(NHW_TEXT);
1877 | 	    if(complain
1878 | #ifndef NO_TERMS
1879 | 		&& nh_CD
1880 | #endif
1881 | 	    ) {
1882 | 		/* attempt to scroll text below map window if there's room */
1883 | 		wins[datawin]->offy = wins[WIN_STATUS]->offy+3;
1884 | 		if((int) wins[datawin]->offy + 12 > (int) ttyDisplay->rows)
1885 | 		    wins[datawin]->offy = 0;
1886 | 	    }
1887 | 	    while (dlb_fgets(buf, BUFSZ, f)) {
1888 | 		if ((cr = index(buf, '\n')) != 0) *cr = 0;
1889 | #ifdef MSDOS
1890 | 		if ((cr = index(buf, '\r')) != 0) *cr = 0;
1891 | #endif
1892 | 		if (index(buf, '\t') != 0) (void) tabexpand(buf);
1893 | 		tty_putstr(datawin, 0, buf);
1894 | 		if(wins[datawin]->flags & WIN_CANCELLED)
1895 | 		    break;
1896 | 	    }
1897 | 	    tty_display_nhwindow(datawin, FALSE);
1898 | 	    tty_destroy_nhwindow(datawin);
1899 | 	    (void) dlb_fclose(f);
1900 | 	}
1901 |     }
1902 | #endif /* DEF_PAGER */
1903 | }
1904 | 
1905 | void
1906 | tty_start_menu(window)
1907 |     winid window;
1908 | {
1909 |     tty_clear_nhwindow(window);
1910 |     return;
1911 | }
1912 | 
1913 | /*ARGSUSED*/
1914 | /*
1915 |  * Add a menu item to the beginning of the menu list.  This list is reversed
1916 |  * later.
1917 |  */
1918 | void
1919 | tty_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
1920 |     winid window;	/* window to use, must be of type NHW_MENU */
1921 |     int glyph;		/* glyph to display with item (unused) */
1922 |     const anything *identifier;	/* what to return if selected */
1923 |     char ch;		/* keyboard accelerator (0 = pick our own) */
1924 |     char gch;		/* group accelerator (0 = no group) */
1925 |     int attr;		/* attribute for string (like tty_putstr()) */
1926 |     const char *str;	/* menu string */
1927 |     boolean preselected; /* item is marked as selected */
1928 | {
1929 |     register struct WinDesc *cw = 0;
1930 |     tty_menu_item *item;
1931 |     const char *newstr;
1932 |     char buf[4+BUFSZ];
1933 | 
1934 |     if (str == (const char*) 0)
1935 | 	return;
1936 | 
1937 |     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
1938 | 		|| cw->type != NHW_MENU)
1939 | 	panic(winpanicstr,  window);
1940 | 
1941 |     cw->nitems++;
1942 |     if (identifier->a_void) {
1943 | 	int len = strlen(str);
1944 | 	if (len >= BUFSZ) {
1945 | 	    /* We *think* everything's coming in off at most BUFSZ bufs... */
1946 | 	    impossible("Menu item too long (%d).", len);
1947 | 	    len = BUFSZ - 1;
1948 | 	}
1949 | 	Sprintf(buf, "%c - ", ch ? ch : '?');
1950 | 	(void) strncpy(buf+4, str, len);
1951 | 	buf[4+len] = '\0';
1952 | 	newstr = buf;
1953 |     } else
1954 | 	newstr = str;
1955 | 
1956 |     item = (tty_menu_item *) alloc(sizeof(tty_menu_item));
1957 |     item->identifier = *identifier;
1958 |     item->count = -1L;
1959 |     item->selected = preselected;
1960 |     item->selector = ch;
1961 |     item->gselector = gch;
1962 |     item->attr = attr;
1963 |     item->str = copy_of(newstr);
1964 | 
1965 |     item->next = cw->mlist;
1966 |     cw->mlist = item;
1967 | }
1968 | 
1969 | /* Invert the given list, can handle NULL as an input. */
1970 | STATIC_OVL tty_menu_item *
1971 | reverse(curr)
1972 |     tty_menu_item *curr;
1973 | {
1974 |     tty_menu_item *next, *head = 0;
1975 | 
1976 |     while (curr) {
1977 | 	next = curr->next;
1978 | 	curr->next = head;
1979 | 	head = curr;
1980 | 	curr = next;
1981 |     }
1982 |     return head;
1983 | }
1984 | 
1985 | /*
1986 |  * End a menu in this window, window must a type NHW_MENU.  This routine
1987 |  * processes the string list.  We calculate the # of pages, then assign
1988 |  * keyboard accelerators as needed.  Finally we decide on the width and
1989 |  * height of the window.
1990 |  */
1991 | void
1992 | tty_end_menu(window, prompt)
1993 |     winid window;	/* menu to use */
1994 |     const char *prompt;	/* prompt to for menu */
1995 | {
1996 |     struct WinDesc *cw = 0;
1997 |     tty_menu_item *curr;
1998 |     short len;
1999 |     int lmax, n;
2000 |     char menu_ch;
2001 | 
2002 |     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0 ||
2003 | 		cw->type != NHW_MENU)
2004 | 	panic(winpanicstr,  window);
2005 | 
2006 |     /* Reverse the list so that items are in correct order. */
2007 |     cw->mlist = reverse(cw->mlist);
2008 | 
2009 |     /* Put the promt at the beginning of the menu. */
2010 |     if (prompt) {
2011 | 	anything any;
2012 | 
2013 | 	any.a_void = 0;	/* not selectable */
2014 | 	tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
2015 | 	tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt, MENU_UNSELECTED);
2016 |     }
2017 | 
2018 |     lmax = min(52, (int)ttyDisplay->rows - 1);		/* # lines per page */
2019 |     cw->npages = (cw->nitems + (lmax - 1)) / lmax;	/* # of pages */
2020 | 
2021 |     /* make sure page list is large enough */
2022 |     if (cw->plist_size < cw->npages+1 /*need 1 slot beyond last*/) {
2023 | 	if (cw->plist) free((genericptr_t)cw->plist);
2024 | 	cw->plist_size = cw->npages + 1;
2025 | 	cw->plist = (tty_menu_item **)
2026 | 			alloc(cw->plist_size * sizeof(tty_menu_item *));
2027 |     }
2028 | 
2029 |     cw->cols = 0; /* cols is set when the win is initialized... (why?) */
2030 |     menu_ch = '?';	/* lint suppression */
2031 |     for (n = 0, curr = cw->mlist; curr; n++, curr = curr->next) {
2032 | 	/* set page boundaries and character accelerators */
2033 | 	if ((n % lmax) == 0) {
2034 | 	    menu_ch = 'a';
2035 | 	    cw->plist[n/lmax] = curr;
2036 | 	}
2037 | 	if (curr->identifier.a_void && !curr->selector) {
2038 | 	    curr->str[0] = curr->selector = menu_ch;
2039 | 	    if (menu_ch++ == 'z') menu_ch = 'A';
2040 | 	}
2041 | 
2042 | 	/* cut off any lines that are too long */
2043 | 	len = strlen(curr->str) + 2;	/* extra space at beg & end */
2044 | 	if (len > (int)ttyDisplay->cols) {
2045 | 	    curr->str[ttyDisplay->cols-2] = 0;
2046 | 	    len = ttyDisplay->cols;
2047 | 	}
2048 | 	if (len > cw->cols) cw->cols = len;
2049 |     }
2050 |     cw->plist[cw->npages] = 0;	/* plist terminator */
2051 | 
2052 |     /*
2053 |      * If greater than 1 page, morestr is "(x of y) " otherwise, "(end) "
2054 |      */
2055 |     if (cw->npages > 1) {
2056 | 	char buf[QBUFSZ];
2057 | 	/* produce the largest demo string */
2058 | 	Sprintf(buf, "(%d of %d) ", cw->npages, cw->npages);
2059 | 	len = strlen(buf);
2060 | 	cw->morestr = copy_of("");
2061 |     } else {
2062 | 	cw->morestr = copy_of("(end) ");
2063 | 	len = strlen(cw->morestr);
2064 |     }
2065 | 
2066 |     if (len > (int)ttyDisplay->cols) {
2067 | 	/* truncate the prompt if its too long for the screen */
2068 | 	if (cw->npages <= 1)	/* only str in single page case */
2069 | 	    cw->morestr[ttyDisplay->cols] = 0;
2070 | 	len = ttyDisplay->cols;
2071 |     }
2072 |     if (len > cw->cols) cw->cols = len;
2073 | 
2074 |     cw->maxcol = cw->cols;
2075 | 
2076 |     /*
2077 |      * The number of lines in the first page plus the morestr will be the
2078 |      * maximum size of the window.
2079 |      */
2080 |     if (cw->npages > 1)
2081 | 	cw->maxrow = cw->rows = lmax + 1;
2082 |     else
2083 | 	cw->maxrow = cw->rows = cw->nitems + 1;
2084 | }
2085 | 
2086 | int
2087 | tty_select_menu(window, how, menu_list)
2088 |     winid window;
2089 |     int how;
2090 |     menu_item **menu_list;
2091 | {
2092 |     register struct WinDesc *cw = 0;
2093 |     tty_menu_item *curr;
2094 |     menu_item *mi;
2095 |     int n, cancelled;
2096 | 
2097 |     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
2098 |        || cw->type != NHW_MENU)
2099 | 	panic(winpanicstr,  window);
2100 | 
2101 |     *menu_list = (menu_item *) 0;
2102 |     cw->how = (short) how;
2103 |     morc = 0;
2104 |     tty_display_nhwindow(window, TRUE);
2105 |     cancelled = !!(cw->flags & WIN_CANCELLED);
2106 |     tty_dismiss_nhwindow(window);	/* does not destroy window data */
2107 | 
2108 |     if (cancelled) {
2109 | 	n = -1;
2110 |     } else {
2111 | 	for (n = 0, curr = cw->mlist; curr; curr = curr->next)
2112 | 	    if (curr->selected) n++;
2113 |     }
2114 | 
2115 |     if (n > 0) {
2116 | 	*menu_list = (menu_item *) alloc(n * sizeof(menu_item));
2117 | 	for (mi = *menu_list, curr = cw->mlist; curr; curr = curr->next)
2118 | 	    if (curr->selected) {
2119 | 		mi->item = curr->identifier;
2120 | 		mi->count = curr->count;
2121 | 		mi++;
2122 | 	    }
2123 |     }
2124 | 
2125 |     return n;
2126 | }
2127 | 
2128 | /* special hack for treating top line --More-- as a one item menu */
2129 | char
2130 | tty_message_menu(let, how, mesg)
2131 | char let;
2132 | int how;
2133 | const char *mesg;
2134 | {
2135 |     /* "menu" without selection; use ordinary pline, no more() */
2136 |     if (how == PICK_NONE) {
2137 | 	pline("%s", mesg);
2138 | 	return 0;
2139 |     }
2140 | 
2141 |     ttyDisplay->dismiss_more = let;
2142 |     morc = 0;
2143 |     /* barebones pline(); since we're only supposed to be called after
2144 |        response to a prompt, we'll assume that the display is up to date */
2145 |     tty_putstr(WIN_MESSAGE, 0, mesg);
2146 |     /* if `mesg' didn't wrap (triggering --More--), force --More-- now */
2147 |     if (ttyDisplay->toplin == 1)
2148 | 	more();
2149 |     /* normally <ESC> means skip further messages, but in this case
2150 |        it means cancel the current prompt; any other messages should
2151 |        continue to be output normally */
2152 |     wins[WIN_MESSAGE]->flags &= ~WIN_CANCELLED;
2153 |     ttyDisplay->dismiss_more = 0;
2154 | 
2155 |     return ((how == PICK_ONE && morc == let) || morc == '\033') ? morc : '\0';
2156 | }
2157 | 
2158 | void
2159 | tty_update_inventory()
2160 | {
2161 |     return;
2162 | }
2163 | 
2164 | void
2165 | tty_mark_synch()
2166 | {
2167 |     (void) fflush(stdout);
2168 | }
2169 | 
2170 | void
2171 | tty_wait_synch()
2172 | {
2173 |     /* we just need to make sure all windows are synch'd */
2174 |     if(!ttyDisplay || ttyDisplay->rawprint) {
2175 | 	getret();
2176 | 	if(ttyDisplay) ttyDisplay->rawprint = 0;
2177 |     } else {
2178 | 	tty_display_nhwindow(WIN_MAP, FALSE);
2179 | 	if(ttyDisplay->inmore) {
2180 | 	    addtopl("--More--");
2181 | 	    (void) fflush(stdout);
2182 | 	} else if(ttyDisplay->inread) {
2183 | 	    /* this can only happen if we were reading and got interrupted */
2184 | 	    ttyDisplay->toplin = 3;
2185 | 	    /* do this twice; 1st time gets the Quit? message again */
2186 | 	    (void) tty_doprev_message();
2187 | 	    (void) tty_doprev_message();
2188 | 	    ttyDisplay->intr++;
2189 | 	    (void) fflush(stdout);
2190 | 	}
2191 |     }
2192 | }
2193 | 
2194 | void
2195 | docorner(xmin, ymax)
2196 |     register int xmin, ymax;
2197 | {
2198 |     register int y;
2199 |     register struct WinDesc *cw = wins[WIN_MAP];
2200 | 
2201 |     if (u.uswallow) {	/* Can be done more efficiently */
2202 | 	swallowed(1);
2203 | 	return;
2204 |     }
2205 | 
2206 | #if defined(SIGWINCH) && defined(CLIPPING)
2207 |     if(ymax > LI) ymax = LI;		/* can happen if window gets smaller */
2208 | #endif
2209 |     for (y = 0; y < ymax; y++) {
2210 | 	tty_curs(BASE_WINDOW, xmin,y);	/* move cursor */
2211 | 	cl_end();			/* clear to end of line */
2212 | #ifdef CLIPPING
2213 | 	if (y<(int) cw->offy || y+clipy > ROWNO)
2214 | 		continue; /* only refresh board */
2215 | #if defined(USE_TILES) && defined(MSDOS)
2216 | 	if (iflags.tile_view)
2217 | 		row_refresh((xmin/2)+clipx-((int)cw->offx/2),COLNO-1,y+clipy-(int)cw->offy);
2218 | 	else
2219 | #endif
2220 | 	row_refresh(xmin+clipx-(int)cw->offx,COLNO-1,y+clipy-(int)cw->offy);
2221 | #else
2222 | 	if (y<cw->offy || y > ROWNO) continue; /* only refresh board  */
2223 | 	row_refresh(xmin-(int)cw->offx,COLNO-1,y-(int)cw->offy);
2224 | #endif
2225 |     }
2226 | 
2227 |     end_glyphout();
2228 |     if (ymax >= (int) wins[WIN_STATUS]->offy) {
2229 | 					/* we have wrecked the bottom line */
2230 | 	flags.botlx = 1;
2231 | 	bot();
2232 |     }
2233 | }
2234 | 
2235 | void
2236 | end_glyphout()
2237 | {
2238 | #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
2239 |     if (GFlag) {
2240 | 	GFlag = FALSE;
2241 | 	graph_off();
2242 |     }
2243 | #endif
2244 | #ifdef TEXTCOLOR
2245 |     if(ttyDisplay->color != NO_COLOR) {
2246 | 	term_end_color();
2247 | 	ttyDisplay->color = NO_COLOR;
2248 |     }
2249 | #endif
2250 | }
2251 | 
2252 | void
2253 | g_putch(in_ch)
2254 | int in_ch;
2255 | {
2256 |     register char ch = (char)in_ch;
2257 | 
2258 | # if defined(ASCIIGRAPH) && !defined(NO_TERMS)
2259 |     if (iflags.IBMgraphics || iflags.eight_bit_tty) {
2260 | 	/* IBM-compatible displays don't need other stuff */
2261 | 	(void) putchar(ch);
2262 |     } else if (ch & 0x80) {
2263 | 	if (!GFlag || HE_resets_AS) {
2264 | 	    graph_on();
2265 | 	    GFlag = TRUE;
2266 | 	}
2267 | 	(void) putchar((ch ^ 0x80)); /* Strip 8th bit */
2268 |     } else {
2269 | 	if (GFlag) {
2270 | 	    graph_off();
2271 | 	    GFlag = FALSE;
2272 | 	}
2273 | 	(void) putchar(ch);
2274 |     }
2275 | 
2276 | #else
2277 |     (void) putchar(ch);
2278 | 
2279 | #endif	/* ASCIIGRAPH && !NO_TERMS */
2280 | 
2281 |     return;
2282 | }
2283 | 
2284 | #ifdef CLIPPING
2285 | void
2286 | setclipped()
2287 | {
2288 | 	clipping = TRUE;
2289 | 	clipx = clipy = 0;
2290 | 	clipxmax = CO;
2291 | 	clipymax = LI - 3;
2292 | }
2293 | 
2294 | void
2295 | tty_cliparound(x, y)
2296 | int x, y;
2297 | {
2298 | 	extern boolean restoring;
2299 | 	int oldx = clipx, oldy = clipy;
2300 | 
2301 | 	if (!clipping) return;
2302 | 	if (x < clipx + 5) {
2303 | 		clipx = max(0, x - 20);
2304 | 		clipxmax = clipx + CO;
2305 | 	}
2306 | 	else if (x > clipxmax - 5) {
2307 | 		clipxmax = min(COLNO, clipxmax + 20);
2308 | 		clipx = clipxmax - CO;
2309 | 	}
2310 | 	if (y < clipy + 2) {
2311 | 		clipy = max(0, y - (clipymax - clipy) / 2);
2312 | 		clipymax = clipy + (LI - 3);
2313 | 	}
2314 | 	else if (y > clipymax - 2) {
2315 | 		clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2);
2316 | 		clipy = clipymax - (LI - 3);
2317 | 	}
2318 | 	if (clipx != oldx || clipy != oldy) {
2319 | 	    if (on_level(&u.uz0, &u.uz) && !restoring)
2320 | 		(void) doredraw();
2321 | 	}
2322 | }
2323 | #endif /* CLIPPING */
2324 | 
2325 | 
2326 | /*
2327 |  *  tty_print_glyph
2328 |  *
2329 |  *  Print the glyph to the output device.  Don't flush the output device.
2330 |  *
2331 |  *  Since this is only called from show_glyph(), it is assumed that the
2332 |  *  position and glyph are always correct (checked there)!
2333 |  */
2334 | void
2335 | tty_print_glyph(window, x, y, glyph)
2336 |     winid window;
2337 |     xchar x, y;
2338 |     int glyph;
2339 | {
2340 |     uchar   ch;
2341 |     register int offset;
2342 |     boolean is_reverse = FALSE;
2343 | #ifdef TEXTCOLOR
2344 |     int	    color;
2345 | 
2346 | #define zap_color(n)  color = iflags.use_color ? zapcolors[n] : NO_COLOR
2347 | #define cmap_color(n) color = iflags.use_color ? defsyms[n].color : NO_COLOR
2348 | #define obj_color(n)  color = iflags.use_color ? objects[n].oc_color : NO_COLOR
2349 | #define mon_color(n)  color = iflags.use_color ? mons[n].mcolor : NO_COLOR
2350 | #define invis_color(n) color = NO_COLOR
2351 | #define pet_color(n)  color = iflags.use_color ? mons[n].mcolor :	      \
2352 | 				/* If no color, try to hilite pets; black  */ \
2353 | 				/* should be nh_HI			   */ \
2354 | 				((iflags.hilite_pet && has_color(CLR_BLACK)) ?     \
2355 | 							CLR_BLACK : NO_COLOR)
2356 | #define warn_color(n) color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR
2357 | # if defined(REINCARNATION) && defined(ASCIIGRAPH)
2358 | #  define ROGUE_COLOR
2359 | # endif
2360 | 
2361 | #else	/* no text color */
2362 | 
2363 | #define zap_color(n)
2364 | #define cmap_color(n)
2365 | #define obj_color(n)
2366 | #define mon_color(n)
2367 | #define invis_color(n)
2368 | #define pet_color(c)
2369 | #define warn_color(n)
2370 | #endif
2371 | 
2372 | #ifdef ROGUE_COLOR
2373 | # if defined(USE_TILES) && defined(MSDOS)
2374 | #define HAS_ROGUE_IBM_GRAPHICS (iflags.IBMgraphics && !iflags.grmode && \
2375 | 	Is_rogue_level(&u.uz))
2376 | # else
2377 | #define HAS_ROGUE_IBM_GRAPHICS (iflags.IBMgraphics && Is_rogue_level(&u.uz))
2378 | # endif
2379 | #endif
2380 | 
2381 | #ifdef CLIPPING
2382 |     if(clipping) {
2383 | 	if(x <= clipx || y < clipy || x >= clipxmax || y >= clipymax)
2384 | 	    return;
2385 |     }
2386 | #endif
2387 |     /*
2388 |      *  Map the glyph back to a character.
2389 |      *
2390 |      *  Warning:  For speed, this makes an assumption on the order of
2391 |      *		  offsets.  The order is set in display.h.
2392 |      */
2393 |     if ((offset = (glyph - GLYPH_WARNING_OFF)) >= 0) {	/* a warning flash */
2394 |     	ch = warnsyms[offset];
2395 | # ifdef ROGUE_COLOR
2396 | 	if (HAS_ROGUE_IBM_GRAPHICS)
2397 | 	    color = NO_COLOR;
2398 | 	else
2399 | # endif
2400 | 	    warn_color(offset);
2401 |     } else if ((offset = (glyph - GLYPH_SWALLOW_OFF)) >= 0) {	/* swallow */
2402 | 	/* see swallow_to_glyph() in display.c */
2403 | 	ch = (uchar) showsyms[S_sw_tl + (offset & 0x7)];
2404 | #ifdef ROGUE_COLOR
2405 | 	if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color)
2406 | 	    color = NO_COLOR;
2407 | 	else
2408 | #endif
2409 | 	    mon_color(offset >> 3);
2410 |     } else if ((offset = (glyph - GLYPH_ZAP_OFF)) >= 0) {	/* zap beam */
2411 | 	/* see zapdir_to_glyph() in display.c */
2412 | 	ch = showsyms[S_vbeam + (offset & 0x3)];
2413 | #ifdef ROGUE_COLOR
2414 | 	if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color)
2415 | 	    color = NO_COLOR;
2416 | 	else
2417 | #endif
2418 | 	    zap_color((offset >> 2));
2419 |     } else if ((offset = (glyph - GLYPH_CMAP_OFF)) >= 0) {	/* cmap */
2420 | 	ch = showsyms[offset];
2421 | #ifdef ROGUE_COLOR
2422 | 	if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color) {
2423 | 	    if (offset >= S_vwall && offset <= S_hcdoor)
2424 | 		color = CLR_BROWN;
2425 | 	    else if (offset >= S_arrow_trap && offset <= S_polymorph_trap)
2426 | 		color = CLR_MAGENTA;
2427 | 	    else if (offset == S_corr || offset == S_litcorr)
2428 | 		color = CLR_GRAY;
2429 | 	    else if (offset >= S_room && offset <= S_water)
2430 | 		color = CLR_GREEN;
2431 | 	    else
2432 | 		color = NO_COLOR;
2433 | 	} else
2434 | #endif
2435 | 	    cmap_color(offset);
2436 |     } else if ((offset = (glyph - GLYPH_OBJ_OFF)) >= 0) {	/* object */
2437 | 	ch = oc_syms[(int)objects[offset].oc_class];
2438 | #ifdef ROGUE_COLOR
2439 | 	if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color) {
2440 | 	    switch(objects[offset].oc_class) {
2441 | 		case GOLD_CLASS: color = CLR_YELLOW; break;
2442 | 		case FOOD_CLASS: color = CLR_RED; break;
2443 | 		default: color = CLR_BRIGHT_BLUE; break;
2444 | 	    }
2445 | 	} else
2446 | #endif
2447 | 	    obj_color(offset);
2448 |     } else if ((offset = (glyph - GLYPH_RIDDEN_OFF)) >= 0) {	/* mon ridden */
2449 | 	ch = monsyms[(int)mons[offset].mlet];
2450 | #ifdef ROGUE_COLOR
2451 | 	if (HAS_ROGUE_IBM_GRAPHICS)
2452 | 	    /* This currently implies that the hero is here -- monsters */
2453 | 	    /* don't ride (yet...).  Should we set it to yellow like in */
2454 | 	    /* the monster case below?  There is no equivalent in rogue. */
2455 | 	    color = NO_COLOR;	/* no need to check iflags.use_color */
2456 | 	else
2457 | #endif
2458 | 	    mon_color(offset);
2459 |     } else if ((offset = (glyph - GLYPH_BODY_OFF)) >= 0) {	/* a corpse */
2460 | 	ch = oc_syms[(int)objects[CORPSE].oc_class];
2461 | #ifdef ROGUE_COLOR
2462 | 	if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color)
2463 | 	    color = CLR_RED;
2464 | 	else
2465 | #endif
2466 | 	    mon_color(offset);
2467 |     } else if ((offset = (glyph - GLYPH_DETECT_OFF)) >= 0) {	/* mon detect */
2468 | 	ch = monsyms[(int)mons[offset].mlet];
2469 | #ifdef ROGUE_COLOR
2470 | 	if (HAS_ROGUE_IBM_GRAPHICS)
2471 | 	    color = NO_COLOR;	/* no need to check iflags.use_color */
2472 | 	else
2473 | #endif
2474 | 	    mon_color(offset);
2475 | 	/* Disabled for now; anyone want to get reverse video to work? */
2476 | 	/* is_reverse = TRUE; */
2477 |     } else if ((offset = (glyph - GLYPH_INVIS_OFF)) >= 0) {	/* invisible */
2478 | 	ch = DEF_INVISIBLE;
2479 | #ifdef ROGUE_COLOR
2480 | 	if (HAS_ROGUE_IBM_GRAPHICS)
2481 | 	    color = NO_COLOR;	/* no need to check iflags.use_color */
2482 | 	else
2483 | #endif
2484 | 	    invis_color(offset);
2485 |     } else if ((offset = (glyph - GLYPH_PET_OFF)) >= 0) {	/* a pet */
2486 | 	ch = monsyms[(int)mons[offset].mlet];
2487 | #ifdef ROGUE_COLOR
2488 | 	if (HAS_ROGUE_IBM_GRAPHICS)
2489 | 	    color = NO_COLOR;	/* no need to check iflags.use_color */
2490 | 	else
2491 | #endif
2492 | 	    pet_color(offset);
2493 |     } else {							/* a monster */
2494 | 	ch = monsyms[(int)mons[glyph].mlet];
2495 | #ifdef ROGUE_COLOR
2496 | 	if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color) {
2497 | 	    if (x == u.ux && y == u.uy)
2498 | 		/* actually player should be yellow-on-gray if in a corridor */
2499 | 		color = CLR_YELLOW;
2500 | 	    else
2501 | 		color = NO_COLOR;
2502 | 	} else
2503 | #endif
2504 | 	    mon_color(glyph);
2505 |     }
2506 | 
2507 |     /* Move the cursor. */
2508 |     tty_curs(window, x,y);
2509 | 
2510 | #ifndef NO_TERMS
2511 |     if (ul_hack && ch == '_') {		/* non-destructive underscore */
2512 | 	(void) putchar((char) ' ');
2513 | 	backsp();
2514 |     }
2515 | #endif
2516 | 
2517 | #ifdef TEXTCOLOR
2518 |     /* Turn off color if no color defined, or rogue level w/o PC graphics. */
2519 | # ifdef REINCARNATION
2520 | #  ifdef ASCIIGRAPH
2521 |     if (!has_color(color) || (Is_rogue_level(&u.uz) && !HAS_ROGUE_IBM_GRAPHICS))
2522 | #  else
2523 |     if (!has_color(color) || Is_rogue_level(&u.uz))
2524 | #  endif
2525 | # else
2526 |     if (!has_color(color))
2527 | # endif
2528 | 	color = NO_COLOR;
2529 | 
2530 |     if (color != ttyDisplay->color) {
2531 | 	if(ttyDisplay->color != NO_COLOR)
2532 | 	    term_end_color();
2533 | 	ttyDisplay->color = color;
2534 | 	if(color != NO_COLOR)
2535 | 	    term_start_color(color);
2536 |     }
2537 | #endif /* TEXTCOLOR */
2538 | #if defined(USE_TILES) && defined(MSDOS)
2539 |     if (iflags.grmode && iflags.tile_view)
2540 |       xputg(glyph,(int)ch);
2541 |     else
2542 | #endif
2543 |     if (is_reverse) { /* not currently working */
2544 | 	term_start_attr(ATR_INVERSE);
2545 | 	g_putch((int)ch);		/* print the character */
2546 | 	term_end_attr(ATR_INVERSE);
2547 |     } else
2548 | 	g_putch((int)ch);		/* print the character */
2549 |     wins[window]->curx++;	/* one character over */
2550 |     ttyDisplay->curx++;		/* the real cursor moved too */
2551 | }
2552 | 
2553 | void
2554 | tty_raw_print(str)
2555 |     const char *str;
2556 | {
2557 |     if(ttyDisplay) ttyDisplay->rawprint++;
2558 | #ifdef MICRO
2559 |     msmsg("%s\n", str);
2560 | #else
2561 |     puts(str); (void) fflush(stdout);
2562 | #endif
2563 | }
2564 | 
2565 | void
2566 | tty_raw_print_bold(str)
2567 |     const char *str;
2568 | {
2569 |     if(ttyDisplay) ttyDisplay->rawprint++;
2570 |     term_start_raw_bold();
2571 | #ifdef MICRO
2572 |     msmsg("%s", str);
2573 | #else
2574 |     (void) fputs(str, stdout);
2575 | #endif
2576 |     term_end_raw_bold();
2577 | #ifdef MICRO
2578 |     msmsg("\n");
2579 | #else
2580 |     puts("");
2581 |     (void) fflush(stdout);
2582 | #endif
2583 | }
2584 | 
2585 | int
2586 | tty_nhgetch()
2587 | {
2588 |     int i;
2589 | #ifdef UNIX
2590 |     /* kludge alert: Some Unix variants return funny values if getc()
2591 |      * is called, interrupted, and then called again.  There
2592 |      * is non-reentrant code in the internal _filbuf() routine, called by
2593 |      * getc().
2594 |      */
2595 |     static volatile int nesting = 0;
2596 |     char nestbuf;
2597 | #endif
2598 | 
2599 |     (void) fflush(stdout);
2600 |     /* Note: if raw_print() and wait_synch() get called to report terminal
2601 |      * initialization problems, then wins[] and ttyDisplay might not be
2602 |      * available yet.  Such problems will probably be fatal before we get
2603 |      * here, but validate those pointers just in case...
2604 |      */
2605 |     if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
2606 | 	    wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
2607 | #ifdef UNIX
2608 |     i = ((++nesting == 1) ? tgetch() :
2609 | 	 (read(fileno(stdin), (genericptr_t)&nestbuf,1) == 1 ? (int)nestbuf :
2610 | 								EOF));
2611 |     --nesting;
2612 | #else
2613 |     i = tgetch();
2614 | #endif
2615 |     if (!i) i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
2616 |     if (ttyDisplay && ttyDisplay->toplin == 1)
2617 | 	ttyDisplay->toplin = 2;
2618 |     return i;
2619 | }
2620 | 
2621 | /*
2622 |  * return a key, or 0, in which case a mouse button was pressed
2623 |  * mouse events should be returned as character postitions in the map window.
2624 |  * Since normal tty's don't have mice, just return a key.
2625 |  */
2626 | /*ARGSUSED*/
2627 | int
2628 | tty_nh_poskey(x, y, mod)
2629 |     int *x, *y, *mod;
2630 | {
2631 | # if defined(WIN32CON)
2632 |     int i;
2633 |     (void) fflush(stdout);
2634 |     /* Note: if raw_print() and wait_synch() get called to report terminal
2635 |      * initialization problems, then wins[] and ttyDisplay might not be
2636 |      * available yet.  Such problems will probably be fatal before we get
2637 |      * here, but validate those pointers just in case...
2638 |      */
2639 |     if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
2640 | 	    wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
2641 |     i = ntposkey(x, y, mod);
2642 |     if (!i && mod && *mod == 0)
2643 |     	i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
2644 |     if (ttyDisplay && ttyDisplay->toplin == 1)
2645 | 		ttyDisplay->toplin = 2;
2646 |     return i;
2647 | # else
2648 |     return tty_nhgetch();
2649 | # endif
2650 | }
2651 | 
2652 | void
2653 | win_tty_init()
2654 | {
2655 | # if defined(WIN32CON)
2656 |     nttty_open();
2657 | # endif
2658 |     return;
2659 | }
2660 | 
2661 | #ifdef POSITIONBAR
2662 | void
2663 | tty_update_positionbar(posbar)
2664 | char *posbar;
2665 | {
2666 | # ifdef MSDOS
2667 | 	video_update_positionbar(posbar);
2668 | # endif
2669 | }
2670 | #endif
2671 | 
2672 | /*
2673 |  * Allocate a copy of the given string.  If null, return a string of
2674 |  * zero length.
2675 |  *
2676 |  * This is an exact duplicate of copy_of() in X11/winmenu.c.
2677 |  */
2678 | static char *
2679 | copy_of(s)
2680 |     const char *s;
2681 | {
2682 |     if (!s) s = "";
2683 |     return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s);
2684 | }
2685 | 
2686 | #endif /* TTY_GRAPHICS */
2687 | 
2688 | /*wintty.c*/