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*/