OSDN Git Service

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