NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / win / tty / topl.c
blob984ad02ecb5eaa4014be2f7d22ee4c7d35c731c4
1 /* aNetHack 0.0.1 topl.c $ANH-Date: 1463787697 2016/05/20 23:41:37 $ $ANH-Branch: aNetHack-3.6.0 $:$ANH-Revision: 1.33 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* aNetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
7 #ifdef TTY_GRAPHICS
9 #include "tcap.h"
10 #include "wintty.h"
12 #ifndef C /* this matches src/cmd.c */
13 #define C(c) (0x1f & (c))
14 #endif
16 STATIC_DCL void FDECL(redotoplin, (const char *));
17 STATIC_DCL void FDECL(topl_putsym, (CHAR_P));
18 STATIC_DCL void NDECL(remember_topl);
19 STATIC_DCL void FDECL(removetopl, (int));
20 STATIC_DCL void FDECL(msghistory_snapshot, (BOOLEAN_P));
21 STATIC_DCL void FDECL(free_msghistory_snapshot, (BOOLEAN_P));
23 int
24 tty_doprev_message()
26 register struct WinDesc *cw = wins[WIN_MESSAGE];
28 winid prevmsg_win;
29 int i;
30 if ((iflags.prevmsg_window != 's')
31 && !ttyDisplay->inread) { /* not single */
32 if (iflags.prevmsg_window == 'f') { /* full */
33 prevmsg_win = create_nhwindow(NHW_MENU);
34 putstr(prevmsg_win, 0, "Message History");
35 putstr(prevmsg_win, 0, "");
36 cw->maxcol = cw->maxrow;
37 i = cw->maxcol;
38 do {
39 if (cw->data[i] && strcmp(cw->data[i], ""))
40 putstr(prevmsg_win, 0, cw->data[i]);
41 i = (i + 1) % cw->rows;
42 } while (i != cw->maxcol);
43 putstr(prevmsg_win, 0, toplines);
44 display_nhwindow(prevmsg_win, TRUE);
45 destroy_nhwindow(prevmsg_win);
46 } else if (iflags.prevmsg_window == 'c') { /* combination */
47 do {
48 morc = 0;
49 if (cw->maxcol == cw->maxrow) {
50 ttyDisplay->dismiss_more = C('p'); /* ^P ok at --More-- */
51 redotoplin(toplines);
52 cw->maxcol--;
53 if (cw->maxcol < 0)
54 cw->maxcol = cw->rows - 1;
55 if (!cw->data[cw->maxcol])
56 cw->maxcol = cw->maxrow;
57 } else if (cw->maxcol == (cw->maxrow - 1)) {
58 ttyDisplay->dismiss_more = C('p'); /* ^P ok at --More-- */
59 redotoplin(cw->data[cw->maxcol]);
60 cw->maxcol--;
61 if (cw->maxcol < 0)
62 cw->maxcol = cw->rows - 1;
63 if (!cw->data[cw->maxcol])
64 cw->maxcol = cw->maxrow;
65 } else {
66 prevmsg_win = create_nhwindow(NHW_MENU);
67 putstr(prevmsg_win, 0, "Message History");
68 putstr(prevmsg_win, 0, "");
69 cw->maxcol = cw->maxrow;
70 i = cw->maxcol;
71 do {
72 if (cw->data[i] && strcmp(cw->data[i], ""))
73 putstr(prevmsg_win, 0, cw->data[i]);
74 i = (i + 1) % cw->rows;
75 } while (i != cw->maxcol);
76 putstr(prevmsg_win, 0, toplines);
77 display_nhwindow(prevmsg_win, TRUE);
78 destroy_nhwindow(prevmsg_win);
81 } while (morc == C('p'));
82 ttyDisplay->dismiss_more = 0;
83 } else { /* reversed */
84 morc = 0;
85 prevmsg_win = create_nhwindow(NHW_MENU);
86 putstr(prevmsg_win, 0, "Message History");
87 putstr(prevmsg_win, 0, "");
88 putstr(prevmsg_win, 0, toplines);
89 cw->maxcol = cw->maxrow - 1;
90 if (cw->maxcol < 0)
91 cw->maxcol = cw->rows - 1;
92 do {
93 putstr(prevmsg_win, 0, cw->data[cw->maxcol]);
94 cw->maxcol--;
95 if (cw->maxcol < 0)
96 cw->maxcol = cw->rows - 1;
97 if (!cw->data[cw->maxcol])
98 cw->maxcol = cw->maxrow;
99 } while (cw->maxcol != cw->maxrow);
101 display_nhwindow(prevmsg_win, TRUE);
102 destroy_nhwindow(prevmsg_win);
103 cw->maxcol = cw->maxrow;
104 ttyDisplay->dismiss_more = 0;
106 } else if (iflags.prevmsg_window == 's') { /* single */
107 ttyDisplay->dismiss_more = C('p'); /* <ctrl/P> allowed at --More-- */
108 do {
109 morc = 0;
110 if (cw->maxcol == cw->maxrow)
111 redotoplin(toplines);
112 else if (cw->data[cw->maxcol])
113 redotoplin(cw->data[cw->maxcol]);
114 cw->maxcol--;
115 if (cw->maxcol < 0)
116 cw->maxcol = cw->rows - 1;
117 if (!cw->data[cw->maxcol])
118 cw->maxcol = cw->maxrow;
119 } while (morc == C('p'));
120 ttyDisplay->dismiss_more = 0;
122 return 0;
125 STATIC_OVL void
126 redotoplin(str)
127 const char *str;
129 int otoplin = ttyDisplay->toplin;
131 home();
132 if (*str & 0x80) {
133 /* kludge for the / command, the only time we ever want a */
134 /* graphics character on the top line */
135 g_putch((int) *str++);
136 ttyDisplay->curx++;
138 end_glyphout(); /* in case message printed during graphics output */
139 putsyms(str);
140 cl_end();
141 ttyDisplay->toplin = 1;
142 if (ttyDisplay->cury && otoplin != 3)
143 more();
146 STATIC_OVL void
147 remember_topl()
149 register struct WinDesc *cw = wins[WIN_MESSAGE];
150 int idx = cw->maxrow;
151 unsigned len = strlen(toplines) + 1;
153 if ((cw->flags & WIN_LOCKHISTORY) || !*toplines)
154 return;
156 if (len > (unsigned) cw->datlen[idx]) {
157 if (cw->data[idx])
158 free(cw->data[idx]);
159 len += (8 - (len & 7)); /* pad up to next multiple of 8 */
160 cw->data[idx] = (char *) alloc(len);
161 cw->datlen[idx] = (short) len;
163 Strcpy(cw->data[idx], toplines);
164 *toplines = '\0';
165 cw->maxcol = cw->maxrow = (idx + 1) % cw->rows;
168 void
169 addtopl(s)
170 const char *s;
172 register struct WinDesc *cw = wins[WIN_MESSAGE];
174 tty_curs(BASE_WINDOW, cw->curx + 1, cw->cury);
175 putsyms(s);
176 cl_end();
177 ttyDisplay->toplin = 1;
180 void
181 more()
183 struct WinDesc *cw = wins[WIN_MESSAGE];
185 /* avoid recursion -- only happens from interrupts */
186 if (ttyDisplay->inmore++)
187 return;
189 if (ttyDisplay->toplin) {
190 tty_curs(BASE_WINDOW, cw->curx + 1, cw->cury);
191 if (cw->curx >= CO - 8)
192 topl_putsym('\n');
195 if (flags.standout)
196 standoutbeg();
197 putsyms(defmorestr);
198 if (flags.standout)
199 standoutend();
201 xwaitforspace("\033 ");
203 if (morc == '\033')
204 cw->flags |= WIN_STOP;
206 if (ttyDisplay->toplin && cw->cury) {
207 docorner(1, cw->cury + 1);
208 cw->curx = cw->cury = 0;
209 home();
210 } else if (morc == '\033') {
211 cw->curx = cw->cury = 0;
212 home();
213 cl_end();
215 ttyDisplay->toplin = 0;
216 ttyDisplay->inmore = 0;
219 void
220 update_topl(bp)
221 register const char *bp;
223 register char *tl, *otl;
224 register int n0;
225 int notdied = 1;
226 struct WinDesc *cw = wins[WIN_MESSAGE];
228 /* If there is room on the line, print message on same line */
229 /* But messages like "You die..." deserve their own line */
230 n0 = strlen(bp);
231 if ((ttyDisplay->toplin == 1 || (cw->flags & WIN_STOP)) && cw->cury == 0
232 && n0 + (int) strlen(toplines) + 3 < CO - 8 /* room for --More-- */
233 && (notdied = strncmp(bp, "You die", 7)) != 0) {
234 Strcat(toplines, " ");
235 Strcat(toplines, bp);
236 cw->curx += 2;
237 if (!(cw->flags & WIN_STOP))
238 addtopl(bp);
239 return;
240 } else if (!(cw->flags & WIN_STOP)) {
241 if (ttyDisplay->toplin == 1) {
242 more();
243 } else if (cw->cury) { /* for when flags.toplin == 2 && cury > 1 */
244 docorner(1, cw->cury + 1); /* reset cury = 0 if redraw screen */
245 cw->curx = cw->cury = 0; /* from home--cls() & docorner(1,n) */
248 remember_topl();
249 (void) strncpy(toplines, bp, TBUFSZ);
250 toplines[TBUFSZ - 1] = 0;
252 for (tl = toplines; n0 >= CO; ) {
253 otl = tl;
254 for (tl += CO - 1; tl != otl; --tl)
255 if (*tl == ' ')
256 break;
257 if (tl == otl) {
258 /* Eek! A huge token. Try splitting after it. */
259 tl = index(otl, ' ');
260 if (!tl)
261 break; /* No choice but to spit it out whole. */
263 *tl++ = '\n';
264 n0 = strlen(tl);
266 if (!notdied)
267 cw->flags &= ~WIN_STOP;
268 if (!(cw->flags & WIN_STOP))
269 redotoplin(toplines);
272 STATIC_OVL
273 void
274 topl_putsym(c)
275 char c;
277 register struct WinDesc *cw = wins[WIN_MESSAGE];
279 if (cw == (struct WinDesc *) 0)
280 panic("Putsym window MESSAGE nonexistant");
282 switch (c) {
283 case '\b':
284 if (ttyDisplay->curx == 0 && ttyDisplay->cury > 0)
285 tty_curs(BASE_WINDOW, CO, (int) ttyDisplay->cury - 1);
286 backsp();
287 ttyDisplay->curx--;
288 cw->curx = ttyDisplay->curx;
289 return;
290 case '\n':
291 cl_end();
292 ttyDisplay->curx = 0;
293 ttyDisplay->cury++;
294 cw->cury = ttyDisplay->cury;
295 #ifdef WIN32CON
296 (void) putchar(c);
297 #endif
298 break;
299 default:
300 if (ttyDisplay->curx == CO - 1)
301 topl_putsym('\n'); /* 1 <= curx < CO; avoid CO */
302 #ifdef WIN32CON
303 (void) putchar(c);
304 #endif
305 ttyDisplay->curx++;
307 cw->curx = ttyDisplay->curx;
308 if (cw->curx == 0)
309 cl_end();
310 #ifndef WIN32CON
311 (void) putchar(c);
312 #endif
315 void
316 putsyms(str)
317 const char *str;
319 while (*str)
320 topl_putsym(*str++);
323 STATIC_OVL void
324 removetopl(n)
325 register int n;
327 /* assume addtopl() has been done, so ttyDisplay->toplin is already set */
328 while (n-- > 0)
329 putsyms("\b \b");
332 extern char erase_char; /* from xxxtty.c; don't need kill_char */
334 char
335 tty_yn_function(query, resp, def)
336 const char *query, *resp;
337 char def;
339 * Generic yes/no function. 'def' is the default (returned by space or
340 * return; 'esc' returns 'q', or 'n', or the default, depending on
341 * what's in the string. The 'query' string is printed before the user
342 * is asked about the string.
343 * If resp is NULL, any single character is accepted and returned.
344 * If not-NULL, only characters in it are allowed (exceptions: the
345 * quitchars are always allowed, and if it contains '#' then digits
346 * are allowed); if it includes an <esc>, anything beyond that won't
347 * be shown in the prompt to the user but will be acceptable as input.
350 register char q;
351 char rtmp[40];
352 boolean digit_ok, allow_num, preserve_case = FALSE;
353 struct WinDesc *cw = wins[WIN_MESSAGE];
354 boolean doprev = 0;
355 char prompt[BUFSZ];
357 if (ttyDisplay->toplin == 1 && !(cw->flags & WIN_STOP))
358 more();
359 cw->flags &= ~WIN_STOP;
360 ttyDisplay->toplin = 3; /* special prompt state */
361 ttyDisplay->inread++;
362 if (resp) {
363 char *rb, respbuf[QBUFSZ];
365 allow_num = (index(resp, '#') != 0);
366 Strcpy(respbuf, resp);
367 /* normally we force lowercase, but if any uppercase letters
368 are present in the allowed response, preserve case;
369 check this before stripping the hidden choices */
370 for (rb = respbuf; *rb; ++rb)
371 if ('A' <= *rb && *rb <= 'Z') {
372 preserve_case = TRUE;
373 break;
375 /* any acceptable responses that follow <esc> aren't displayed */
376 if ((rb = index(respbuf, '\033')) != 0)
377 *rb = '\0';
378 (void) strncpy(prompt, query, QBUFSZ - 1);
379 prompt[QBUFSZ - 1] = '\0';
380 Sprintf(eos(prompt), " [%s]", respbuf);
381 if (def)
382 Sprintf(eos(prompt), " (%c)", def);
383 /* not pline("%s ", prompt);
384 trailing space is wanted here in case of reprompt */
385 Strcat(prompt, " ");
386 pline("%s", prompt);
387 } else {
388 /* no restriction on allowed response, so always preserve case */
389 /* preserve_case = TRUE; -- moot since we're jumping to the end */
390 pline("%s ", query);
391 q = readchar();
392 goto clean_up;
395 do { /* loop until we get valid input */
396 q = readchar();
397 if (!preserve_case)
398 q = lowc(q);
399 if (q == '\020') { /* ctrl-P */
400 if (iflags.prevmsg_window != 's') {
401 int sav = ttyDisplay->inread;
402 ttyDisplay->inread = 0;
403 (void) tty_doprev_message();
404 ttyDisplay->inread = sav;
405 tty_clear_nhwindow(WIN_MESSAGE);
406 cw->maxcol = cw->maxrow;
407 addtopl(prompt);
408 } else {
409 if (!doprev)
410 (void) tty_doprev_message(); /* need two initially */
411 (void) tty_doprev_message();
412 doprev = 1;
414 q = '\0'; /* force another loop iteration */
415 continue;
416 } else if (doprev) {
417 /* BUG[?]: this probably ought to check whether the
418 character which has just been read is an acceptable
419 response; if so, skip the reprompt and use it. */
420 tty_clear_nhwindow(WIN_MESSAGE);
421 cw->maxcol = cw->maxrow;
422 doprev = 0;
423 addtopl(prompt);
424 q = '\0'; /* force another loop iteration */
425 continue;
427 digit_ok = allow_num && digit(q);
428 if (q == '\033') {
429 if (index(resp, 'q'))
430 q = 'q';
431 else if (index(resp, 'n'))
432 q = 'n';
433 else
434 q = def;
435 break;
436 } else if (index(quitchars, q)) {
437 q = def;
438 break;
440 if (!index(resp, q) && !digit_ok) {
441 tty_nhbell();
442 q = (char) 0;
443 } else if (q == '#' || digit_ok) {
444 char z, digit_string[2];
445 int n_len = 0;
446 long value = 0;
447 addtopl("#"), n_len++;
448 digit_string[1] = '\0';
449 if (q != '#') {
450 digit_string[0] = q;
451 addtopl(digit_string), n_len++;
452 value = q - '0';
453 q = '#';
455 do { /* loop until we get a non-digit */
456 z = readchar();
457 if (!preserve_case)
458 z = lowc(z);
459 if (digit(z)) {
460 value = (10 * value) + (z - '0');
461 if (value < 0)
462 break; /* overflow: try again */
463 digit_string[0] = z;
464 addtopl(digit_string), n_len++;
465 } else if (z == 'y' || index(quitchars, z)) {
466 if (z == '\033')
467 value = -1; /* abort */
468 z = '\n'; /* break */
469 } else if (z == erase_char || z == '\b') {
470 if (n_len <= 1) {
471 value = -1;
472 break;
473 } else {
474 value /= 10;
475 removetopl(1), n_len--;
477 } else {
478 value = -1; /* abort */
479 tty_nhbell();
480 break;
482 } while (z != '\n');
483 if (value > 0)
484 yn_number = value;
485 else if (value == 0)
486 q = 'n'; /* 0 => "no" */
487 else { /* remove number from top line, then try again */
488 removetopl(n_len), n_len = 0;
489 q = '\0';
492 } while (!q);
494 if (q != '#') {
495 Sprintf(rtmp, "%c", q);
496 addtopl(rtmp);
498 clean_up:
499 ttyDisplay->inread--;
500 ttyDisplay->toplin = 2;
501 if (ttyDisplay->intr)
502 ttyDisplay->intr--;
503 if (wins[WIN_MESSAGE]->cury)
504 tty_clear_nhwindow(WIN_MESSAGE);
506 return q;
509 /* shared by tty_getmsghistory() and tty_putmsghistory() */
510 static char **snapshot_mesgs = 0;
512 /* collect currently available message history data into a sequential array;
513 optionally, purge that data from the active circular buffer set as we go */
514 STATIC_OVL void
515 msghistory_snapshot(purge)
516 boolean purge; /* clear message history buffer as we copy it */
518 char *mesg;
519 int i, inidx, outidx;
520 struct WinDesc *cw;
522 /* paranoia (too early or too late panic save attempt?) */
523 if (WIN_MESSAGE == WIN_ERR || !wins[WIN_MESSAGE])
524 return;
525 cw = wins[WIN_MESSAGE];
527 /* flush toplines[], moving most recent message to history */
528 remember_topl();
530 /* for a passive snapshot, we just copy pointers, so can't allow further
531 history updating to take place because that could clobber them */
532 if (!purge)
533 cw->flags |= WIN_LOCKHISTORY;
535 snapshot_mesgs = (char **) alloc((cw->rows + 1) * sizeof(char *));
536 outidx = 0;
537 inidx = cw->maxrow;
538 for (i = 0; i < cw->rows; ++i) {
539 snapshot_mesgs[i] = (char *) 0;
540 mesg = cw->data[inidx];
541 if (mesg && *mesg) {
542 snapshot_mesgs[outidx++] = mesg;
543 if (purge) {
544 /* we're taking this pointer away; subsequest history
545 updates will eventually allocate a new one to replace it */
546 cw->data[inidx] = (char *) 0;
547 cw->datlen[inidx] = 0;
550 inidx = (inidx + 1) % cw->rows;
552 snapshot_mesgs[cw->rows] = (char *) 0; /* sentinel */
554 /* for a destructive snapshot, history is now completely empty */
555 if (purge)
556 cw->maxcol = cw->maxrow = 0;
559 /* release memory allocated to message history snapshot */
560 STATIC_OVL void
561 free_msghistory_snapshot(purged)
562 boolean purged; /* True: took history's pointers, False: just cloned them */
564 if (snapshot_mesgs) {
565 /* snapshot pointers are no longer in use */
566 if (purged) {
567 int i;
569 for (i = 0; snapshot_mesgs[i]; ++i)
570 free((genericptr_t) snapshot_mesgs[i]);
573 free((genericptr_t) snapshot_mesgs), snapshot_mesgs = (char **) 0;
575 /* history can resume being updated at will now... */
576 if (!purged)
577 wins[WIN_MESSAGE]->flags &= ~WIN_LOCKHISTORY;
582 * This is called by the core save routines.
583 * Each time we are called, we return one string from the
584 * message history starting with the oldest message first.
585 * When none are left, we return a final null string.
587 * History is collected at the time of the first call.
588 * Any new messages issued after that point will not be
589 * included among the output of the subsequent calls.
591 char *
592 tty_getmsghistory(init)
593 boolean init;
595 static int nxtidx;
596 char *nextmesg;
597 char *result = 0;
599 if (init) {
600 msghistory_snapshot(FALSE);
601 nxtidx = 0;
604 if (snapshot_mesgs) {
605 nextmesg = snapshot_mesgs[nxtidx++];
606 if (nextmesg) {
607 result = (char *) nextmesg;
608 } else {
609 free_msghistory_snapshot(FALSE);
612 return result;
616 * This is called by the core savefile restore routines.
617 * Each time we are called, we stuff the string into our message
618 * history recall buffer. The core will send the oldest message
619 * first (actually it sends them in the order they exist in the
620 * save file, but that is supposed to be the oldest first).
621 * These messages get pushed behind any which have been issued
622 * since this session with the program has been started, since
623 * they come from a previous session and logically precede
624 * anything (like "Restoring save file...") that's happened now.
626 * Called with a null pointer to finish up restoration.
628 * It's also called by the quest pager code when a block message
629 * has a one-line summary specified. We put that line directly
630 * message history for ^P recall without having displayed it.
632 void
633 tty_putmsghistory(msg, restoring_msghist)
634 const char *msg;
635 boolean restoring_msghist;
637 static boolean initd = FALSE;
638 int idx;
640 if (restoring_msghist && !initd) {
641 /* we're restoring history from the previous session, but new
642 messages have already been issued this session ("Restoring...",
643 for instance); collect current history (ie, those new messages),
644 and also clear it out so that nothing will be present when the
645 restored ones are being put into place */
646 msghistory_snapshot(TRUE);
647 initd = TRUE;
650 if (msg) {
651 /* move most recent message to history, make this become most recent
653 remember_topl();
654 Strcpy(toplines, msg);
655 } else if (snapshot_mesgs) {
656 /* done putting arbitrary messages in; put the snapshot ones back */
657 for (idx = 0; snapshot_mesgs[idx]; ++idx) {
658 remember_topl();
659 Strcpy(toplines, snapshot_mesgs[idx]);
661 /* now release the snapshot */
662 free_msghistory_snapshot(TRUE);
663 initd = FALSE; /* reset */
667 #endif /* TTY_GRAPHICS */
669 /*topl.c*/