1 /* NetHack 3.6 topl.c $NHDT-Date: 1560608320 2019/06/15 14:18:40 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.47 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Michael Allison, 2009. */
4 /* NetHack may be freely redistributed. See license for details. */
13 #ifndef C /* this matches src/cmd.c */
14 #define C(c) (0x1f & (c))
17 STATIC_DCL void FDECL(redotoplin, (const char *));
18 STATIC_DCL void FDECL(topl_putsym, (CHAR_P));
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));
26 register struct WinDesc *cw = wins[WIN_MESSAGE];
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;
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 */
49 if (cw->maxcol == cw->maxrow) {
50 ttyDisplay->dismiss_more = C('p'); /* ^P ok at --More-- */
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]);
62 cw->maxcol = cw->rows - 1;
63 if (!cw->data[cw->maxcol])
64 cw->maxcol = cw->maxrow;
66 prevmsg_win = create_nhwindow(NHW_MENU);
67 putstr(prevmsg_win, 0, "Message History");
68 putstr(prevmsg_win, 0, "");
69 cw->maxcol = cw->maxrow;
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 */
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;
91 cw->maxcol = cw->rows - 1;
93 putstr(prevmsg_win, 0, cw->data[cw->maxcol]);
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-- */
110 if (cw->maxcol == cw->maxrow)
111 redotoplin(toplines);
112 else if (cw->data[cw->maxcol])
113 redotoplin(cw->data[cw->maxcol]);
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;
129 int otoplin = ttyDisplay->toplin;
134 /* kludge for the / command, the only time we ever want a */
135 /* graphics character on the top line */
136 g_putch((int) *str++);
140 end_glyphout(); /* in case message printed during graphics output */
143 ttyDisplay->toplin = 1;
144 if (ttyDisplay->cury && otoplin != 3)
148 /* for use by tty_putstr() */
153 struct WinDesc *cw = wins[WIN_MESSAGE];
155 if (!(cw->flags & WIN_STOP)) {
156 if (ttyDisplay->cury && ttyDisplay->toplin == 2)
157 tty_clear_nhwindow(WIN_MESSAGE);
159 cw->curx = cw->cury = 0;
164 if (ttyDisplay->cury && ttyDisplay->toplin != 3)
165 ttyDisplay->toplin = 2;
169 /* used by update_topl(); also by tty_putstr() */
173 register struct WinDesc *cw = wins[WIN_MESSAGE];
174 int idx = cw->maxrow;
175 unsigned len = strlen(toplines) + 1;
177 if ((cw->flags & WIN_LOCKHISTORY) || !*toplines)
180 if (len > (unsigned) cw->datlen[idx]) {
183 len += (8 - (len & 7)); /* pad up to next multiple of 8 */
184 cw->data[idx] = (char *) alloc(len);
185 cw->datlen[idx] = (short) len;
187 Strcpy(cw->data[idx], toplines);
189 cw->maxcol = cw->maxrow = (idx + 1) % cw->rows;
196 register struct WinDesc *cw = wins[WIN_MESSAGE];
198 tty_curs(BASE_WINDOW, cw->curx + 1, cw->cury);
201 ttyDisplay->toplin = 1;
207 struct WinDesc *cw = wins[WIN_MESSAGE];
209 /* avoid recursion -- only happens from interrupts */
210 if (ttyDisplay->inmore++)
212 if (iflags.debug_fuzzer)
215 if (ttyDisplay->toplin) {
216 tty_curs(BASE_WINDOW, cw->curx + 1, cw->cury);
217 if (cw->curx >= CO - 8)
227 xwaitforspace("\033 ");
230 cw->flags |= WIN_STOP;
232 if (ttyDisplay->toplin && cw->cury) {
233 docorner(1, cw->cury + 1);
234 cw->curx = cw->cury = 0;
236 } else if (morc == '\033') {
237 cw->curx = cw->cury = 0;
241 ttyDisplay->toplin = 0;
242 ttyDisplay->inmore = 0;
246 folding_japanese( str, pos )
250 char ss[1024],s1[1024],s2[1024];
251 static char newstr[1024]; /* may be enough */
256 split_japanese(ss, s1, s2, pos);
260 Strcat(newstr, "\n");
270 register const char *bp;
273 register char *tl, *otl;
277 struct WinDesc *cw = wins[WIN_MESSAGE];
279 /* If there is room on the line, print message on same line */
280 /* But messages like "You die..." deserve their own line */
282 if ((ttyDisplay->toplin == 1 || (cw->flags & WIN_STOP))
284 && n0 + (int) strlen(toplines) + 3 < CO - 8 /* room for --More-- */
285 && (notdied = strncmp(bp, "You die", 7)) != 0) {
286 Strcat(toplines, " ");
287 Strcat(toplines, bp);
289 if (!(cw->flags & WIN_STOP))
292 } else if (!(cw->flags & WIN_STOP)) {
293 if (ttyDisplay->toplin == 1) {
295 } else if (cw->cury) { /* for when flags.toplin == 2 && cury > 1 */
296 docorner(1, cw->cury + 1); /* reset cury = 0 if redraw screen */
297 cw->curx = cw->cury = 0; /* from home--cls() & docorner(1,n) */
303 Strcpy(toplines, bp);
305 Strcpy(toplines,folding_japanese(bp, CO-2));
307 (void) strncpy(toplines, bp, TBUFSZ);
308 toplines[TBUFSZ - 1] = 0;
310 for (tl = toplines; n0 >= CO; ) {
312 for (tl += CO - 1; tl != otl; --tl)
316 /* Eek! A huge token. Try splitting after it. */
317 tl = index(otl, ' ');
319 break; /* No choice but to spit it out whole. */
326 cw->flags &= ~WIN_STOP;
327 if (!(cw->flags & WIN_STOP))
328 redotoplin(toplines);
336 register struct WinDesc *cw = wins[WIN_MESSAGE];
338 unsigned char uc = *((unsigned char *)(&c));
341 if (cw == (struct WinDesc *) 0)
342 panic("Putsym window MESSAGE nonexistent");
346 if (ttyDisplay->curx == 0 && ttyDisplay->cury > 0)
347 tty_curs(BASE_WINDOW, CO, (int) ttyDisplay->cury - 1);
350 cw->curx = ttyDisplay->curx;
355 (void) jputchar('\r'); /* raw mode
\82Ã…
\95K
\97v? */
356 (void) jputchar('\n');
358 ttyDisplay->curx = 0;
360 cw->cury = ttyDisplay->cury;
368 if (ttyDisplay->curx == CO - 1)
369 topl_putsym('\n'); /* 1 <= curx < CO; avoid CO */
376 cw->curx = ttyDisplay->curx;
377 if(cw->curx == 0) cl_end();
378 (void) jputchar((unsigned char)uc);
384 cw->curx = ttyDisplay->curx;
403 ** do not translate kcode this function(see topl_putsym)
410 register struct WinDesc *cw = wins[WIN_MESSAGE];
412 if(cw == (struct WinDesc *) 0) panic("Putsym window MESSAGE nonexistant");
416 if(ttyDisplay->curx == 0 && ttyDisplay->cury > 0)
417 tty_curs(BASE_WINDOW, CO, (int)ttyDisplay->cury-1);
420 cw->curx = ttyDisplay->curx;
425 (void) cputchar('\r'); /* raw mode
\82Ã…
\95K
\97v? */
426 (void) cputchar('\n');
428 ttyDisplay->curx = 0;
430 cw->cury = ttyDisplay->cury;
433 if(ttyDisplay->curx == CO-1)
434 topl_putsym('\n'); /* 1 <= curx <= CO; avoid CO */
438 cw->curx = ttyDisplay->curx;
439 if(cw->curx == 0) cl_end();
447 ** do not translate kcode this function(see putsym)
454 raw_topl_putsym(*str++);
462 /* assume addtopl() has been done, so ttyDisplay->toplin is already set */
467 extern char erase_char; /* from xxxtty.c; don't need kill_char */
469 /* returns a single keystroke; also sets 'yn_number' */
471 tty_yn_function(query, resp, def)
472 const char *query, *resp;
475 * Generic yes/no function. 'def' is the default (returned by space or
476 * return; 'esc' returns 'q', or 'n', or the default, depending on
477 * what's in the string. The 'query' string is printed before the user
478 * is asked about the string.
479 * If resp is NULL, any single character is accepted and returned.
480 * If not-NULL, only characters in it are allowed (exceptions: the
481 * quitchars are always allowed, and if it contains '#' then digits
482 * are allowed); if it includes an <esc>, anything beyond that won't
483 * be shown in the prompt to the user but will be acceptable as input.
488 boolean digit_ok, allow_num, preserve_case = FALSE;
489 struct WinDesc *cw = wins[WIN_MESSAGE];
494 if (ttyDisplay->toplin == 1 && !(cw->flags & WIN_STOP))
496 cw->flags &= ~WIN_STOP;
497 ttyDisplay->toplin = 3; /* special prompt state */
498 ttyDisplay->inread++;
500 char *rb, respbuf[QBUFSZ];
502 allow_num = (index(resp, '#') != 0);
503 Strcpy(respbuf, resp);
504 /* normally we force lowercase, but if any uppercase letters
505 are present in the allowed response, preserve case;
506 check this before stripping the hidden choices */
507 for (rb = respbuf; *rb; ++rb)
508 if ('A' <= *rb && *rb <= 'Z') {
509 preserve_case = TRUE;
512 /* any acceptable responses that follow <esc> aren't displayed */
513 if ((rb = index(respbuf, '\033')) != 0)
515 (void) strncpy(prompt, query, QBUFSZ - 1);
516 prompt[QBUFSZ - 1] = '\0';
517 Sprintf(eos(prompt), " [%s]", respbuf);
519 Sprintf(eos(prompt), " (%c)", def);
520 /* not pline("%s ", prompt);
521 trailing space is wanted here in case of reprompt */
523 custompline(OVERRIDE_MSGTYPE | SUPPRESS_HISTORY, "%s", prompt);
525 /* no restriction on allowed response, so always preserve case */
526 /* preserve_case = TRUE; -- moot since we're jumping to the end */
527 Sprintf(prompt, "%s ", query);
528 custompline(OVERRIDE_MSGTYPE | SUPPRESS_HISTORY, "%s", prompt);
533 do { /* loop until we get valid input */
537 if (q == '\020') { /* ctrl-P */
538 if (iflags.prevmsg_window != 's') {
539 int sav = ttyDisplay->inread;
540 ttyDisplay->inread = 0;
541 (void) tty_doprev_message();
542 ttyDisplay->inread = sav;
543 tty_clear_nhwindow(WIN_MESSAGE);
544 cw->maxcol = cw->maxrow;
548 (void) tty_doprev_message(); /* need two initially */
549 (void) tty_doprev_message();
552 q = '\0'; /* force another loop iteration */
555 /* BUG[?]: this probably ought to check whether the
556 character which has just been read is an acceptable
557 response; if so, skip the reprompt and use it. */
558 tty_clear_nhwindow(WIN_MESSAGE);
559 cw->maxcol = cw->maxrow;
562 q = '\0'; /* force another loop iteration */
565 digit_ok = allow_num && digit(q);
567 if (index(resp, 'q'))
569 else if (index(resp, 'n'))
574 } else if (index(quitchars, q)) {
578 if (!index(resp, q) && !digit_ok) {
581 } else if (q == '#' || digit_ok) {
582 char z, digit_string[2];
586 addtopl("#"), n_len++;
587 digit_string[1] = '\0';
590 addtopl(digit_string), n_len++;
594 do { /* loop until we get a non-digit */
599 value = (10 * value) + (z - '0');
601 break; /* overflow: try again */
603 addtopl(digit_string), n_len++;
604 } else if (z == 'y' || index(quitchars, z)) {
606 value = -1; /* abort */
607 z = '\n'; /* break */
608 } else if (z == erase_char || z == '\b') {
614 removetopl(1), n_len--;
617 value = -1; /* abort */
625 q = 'n'; /* 0 => "no" */
626 else { /* remove number from top line, then try again */
627 removetopl(n_len), n_len = 0;
635 Sprintf(rtmp, "#%ld", yn_number);
637 (void) key2txt(q, rtmp);
638 /* addtopl(rtmp); -- rewrite toplines instead */
639 Sprintf(toplines, "%s%s", prompt, rtmp);
641 dumplogmsg(toplines);
643 ttyDisplay->inread--;
644 ttyDisplay->toplin = 2;
645 if (ttyDisplay->intr)
647 if (wins[WIN_MESSAGE]->cury)
648 tty_clear_nhwindow(WIN_MESSAGE);
653 /* shared by tty_getmsghistory() and tty_putmsghistory() */
654 static char **snapshot_mesgs = 0;
656 /* collect currently available message history data into a sequential array;
657 optionally, purge that data from the active circular buffer set as we go */
659 msghistory_snapshot(purge)
660 boolean purge; /* clear message history buffer as we copy it */
663 int i, inidx, outidx;
666 /* paranoia (too early or too late panic save attempt?) */
667 if (WIN_MESSAGE == WIN_ERR || !wins[WIN_MESSAGE])
669 cw = wins[WIN_MESSAGE];
671 /* flush toplines[], moving most recent message to history */
674 /* for a passive snapshot, we just copy pointers, so can't allow further
675 history updating to take place because that could clobber them */
677 cw->flags |= WIN_LOCKHISTORY;
679 snapshot_mesgs = (char **) alloc((cw->rows + 1) * sizeof(char *));
682 for (i = 0; i < cw->rows; ++i) {
683 snapshot_mesgs[i] = (char *) 0;
684 mesg = cw->data[inidx];
686 snapshot_mesgs[outidx++] = mesg;
688 /* we're taking this pointer away; subsequest history
689 updates will eventually allocate a new one to replace it */
690 cw->data[inidx] = (char *) 0;
691 cw->datlen[inidx] = 0;
694 inidx = (inidx + 1) % cw->rows;
696 snapshot_mesgs[cw->rows] = (char *) 0; /* sentinel */
698 /* for a destructive snapshot, history is now completely empty */
700 cw->maxcol = cw->maxrow = 0;
703 /* release memory allocated to message history snapshot */
705 free_msghistory_snapshot(purged)
706 boolean purged; /* True: took history's pointers, False: just cloned them */
708 if (snapshot_mesgs) {
709 /* snapshot pointers are no longer in use */
713 for (i = 0; snapshot_mesgs[i]; ++i)
714 free((genericptr_t) snapshot_mesgs[i]);
717 free((genericptr_t) snapshot_mesgs), snapshot_mesgs = (char **) 0;
719 /* history can resume being updated at will now... */
721 wins[WIN_MESSAGE]->flags &= ~WIN_LOCKHISTORY;
726 * This is called by the core save routines.
727 * Each time we are called, we return one string from the
728 * message history starting with the oldest message first.
729 * When none are left, we return a final null string.
731 * History is collected at the time of the first call.
732 * Any new messages issued after that point will not be
733 * included among the output of the subsequent calls.
736 tty_getmsghistory(init)
744 msghistory_snapshot(FALSE);
748 if (snapshot_mesgs) {
749 nextmesg = snapshot_mesgs[nxtidx++];
751 result = (char *) nextmesg;
753 free_msghistory_snapshot(FALSE);
760 * This is called by the core savefile restore routines.
761 * Each time we are called, we stuff the string into our message
762 * history recall buffer. The core will send the oldest message
763 * first (actually it sends them in the order they exist in the
764 * save file, but that is supposed to be the oldest first).
765 * These messages get pushed behind any which have been issued
766 * since this session with the program has been started, since
767 * they come from a previous session and logically precede
768 * anything (like "Restoring save file...") that's happened now.
770 * Called with a null pointer to finish up restoration.
772 * It's also called by the quest pager code when a block message
773 * has a one-line summary specified. We put that line directly
774 * into message history for ^P recall without having displayed it.
777 tty_putmsghistory(msg, restoring_msghist)
779 boolean restoring_msghist;
781 static boolean initd = FALSE;
784 extern unsigned saved_pline_index; /* pline.c */
787 if (restoring_msghist && !initd) {
788 /* we're restoring history from the previous session, but new
789 messages have already been issued this session ("Restoring...",
790 for instance); collect current history (ie, those new messages),
791 and also clear it out so that nothing will be present when the
792 restored ones are being put into place */
793 msghistory_snapshot(TRUE);
796 /* this suffices; there's no need to scrub saved_pline[] pointers */
797 saved_pline_index = 0;
802 /* move most recent message to history, make this become most recent */
804 Strcpy(toplines, msg);
806 dumplogmsg(toplines);
808 } else if (snapshot_mesgs) {
809 /* done putting arbitrary messages in; put the snapshot ones back */
810 for (idx = 0; snapshot_mesgs[idx]; ++idx) {
812 Strcpy(toplines, snapshot_mesgs[idx]);
814 dumplogmsg(toplines);
817 /* now release the snapshot */
818 free_msghistory_snapshot(TRUE);
819 initd = FALSE; /* reset */
823 #endif /* TTY_GRAPHICS */