1/*	SCCS Id: @(#)topl.c	3.4	1996/10/24	*/
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
17STATIC_DCL void FDECL(redotoplin, (const char*));
18STATIC_DCL void FDECL(topl_putsym, (CHAR_P));
19STATIC_DCL void NDECL(remember_topl);
20STATIC_DCL void FDECL(removetopl, (int));
21
22#ifdef OVLB
23
24int
25tty_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') && !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');	/* <ctrl/P> allowed at --More-- */
51                    redotoplin(toplines);
52                    cw->maxcol--;
53                    if (cw->maxcol < 0) cw->maxcol = cw->rows-1;
54                    if (!cw->data[cw->maxcol])
55                        cw->maxcol = cw->maxrow;
56                } else if (cw->maxcol == (cw->maxrow - 1)){
57                    ttyDisplay->dismiss_more = C('p');	/* <ctrl/P> allowed at --More-- */
58                    redotoplin(cw->data[cw->maxcol]);
59                    cw->maxcol--;
60                    if (cw->maxcol < 0) cw->maxcol = cw->rows-1;
61                    if (!cw->data[cw->maxcol])
62                        cw->maxcol = cw->maxrow;
63                } else {
64                    prevmsg_win = create_nhwindow(NHW_MENU);
65                    putstr(prevmsg_win, 0, "Message History");
66                    putstr(prevmsg_win, 0, "");
67                    cw->maxcol = cw->maxrow;
68                    i = cw->maxcol;
69                    do {
70                        if(cw->data[i] && strcmp(cw->data[i], "") )
71                            putstr(prevmsg_win, 0, cw->data[i]);
72                        i = (i + 1) % cw->rows;
73                    } while (i != cw->maxcol);
74                    putstr(prevmsg_win, 0, toplines);
75                    display_nhwindow(prevmsg_win, TRUE);
76                    destroy_nhwindow(prevmsg_win);
77                }
78
79            } while (morc == C('p'));
80            ttyDisplay->dismiss_more = 0;
81        } else { /* reversed */
82            morc = 0;
83            prevmsg_win = create_nhwindow(NHW_MENU);
84            putstr(prevmsg_win, 0, "Message History");
85            putstr(prevmsg_win, 0, "");
86            putstr(prevmsg_win, 0, toplines);
87            cw->maxcol=cw->maxrow-1;
88            if(cw->maxcol < 0) cw->maxcol = cw->rows-1;
89            do {
90                putstr(prevmsg_win, 0, cw->data[cw->maxcol]);
91                cw->maxcol--;
92                if (cw->maxcol < 0) cw->maxcol = cw->rows-1;
93                if (!cw->data[cw->maxcol])
94                    cw->maxcol = cw->maxrow;
95            } while (cw->maxcol != cw->maxrow);
96
97            display_nhwindow(prevmsg_win, TRUE);
98            destroy_nhwindow(prevmsg_win);
99            cw->maxcol = cw->maxrow;
100            ttyDisplay->dismiss_more = 0;
101        }
102    } else if(iflags.prevmsg_window == 's') { /* single */
103        ttyDisplay->dismiss_more = C('p');  /* <ctrl/P> allowed at --More-- */
104        do {
105            morc = 0;
106            if (cw->maxcol == cw->maxrow)
107                redotoplin(toplines);
108            else if (cw->data[cw->maxcol])
109                redotoplin(cw->data[cw->maxcol]);
110            cw->maxcol--;
111            if (cw->maxcol < 0) cw->maxcol = cw->rows-1;
112            if (!cw->data[cw->maxcol])
113                cw->maxcol = cw->maxrow;
114        } while (morc == C('p'));
115        ttyDisplay->dismiss_more = 0;
116    }
117    return 0;
118}
119
120#endif /* OVLB */
121#ifdef OVL1
122
123STATIC_OVL void
124redotoplin(str)
125    const char *str;
126{
127	int otoplin = ttyDisplay->toplin;
128	home();
129	if(*str & 0x80) {
130		/* kludge for the / command, the only time we ever want a */
131		/* graphics character on the top line */
132		g_putch((int)*str++);
133		ttyDisplay->curx++;
134	}
135	end_glyphout();	/* in case message printed during graphics output */
136	putsyms(str);
137	cl_end();
138	ttyDisplay->toplin = 1;
139	if(ttyDisplay->cury && otoplin != 3)
140		more();
141}
142
143STATIC_OVL void
144remember_topl()
145{
146    register struct WinDesc *cw = wins[WIN_MESSAGE];
147    int idx = cw->maxrow;
148    unsigned len = strlen(toplines) + 1;
149
150    if (len > (unsigned)cw->datlen[idx]) {
151	if (cw->data[idx]) free(cw->data[idx]);
152	len += (8 - (len & 7));		/* pad up to next multiple of 8 */
153	cw->data[idx] = (char *)alloc(len);
154	cw->datlen[idx] = (short)len;
155    }
156    Strcpy(cw->data[idx], toplines);
157    cw->maxcol = cw->maxrow = (idx + 1) % cw->rows;
158}
159
160void
161addtopl(s)
162const char *s;
163{
164    register struct WinDesc *cw = wins[WIN_MESSAGE];
165
166    tty_curs(BASE_WINDOW,cw->curx+1,cw->cury);
167    putsyms(s);
168    cl_end();
169    ttyDisplay->toplin = 1;
170}
171
172#endif /* OVL1 */
173#ifdef OVL2
174
175void
176more()
177{
178    struct WinDesc *cw = wins[WIN_MESSAGE];
179
180    /* avoid recursion -- only happens from interrupts */
181    if(ttyDisplay->inmore++)
182	return;
183
184    if(ttyDisplay->toplin) {
185	tty_curs(BASE_WINDOW, cw->curx+1, cw->cury);
186	if(cw->curx >= CO - 8) topl_putsym('\n');
187    }
188
189    if(flags.standout)
190	standoutbeg();
191    putsyms(defmorestr);
192    if(flags.standout)
193	standoutend();
194
195    xwaitforspace("\033 ");
196
197    if(morc == '\033')
198	cw->flags |= WIN_STOP;
199
200    if(ttyDisplay->toplin && cw->cury) {
201	docorner(1, cw->cury+1);
202	cw->curx = cw->cury = 0;
203	home();
204    } else if(morc == '\033') {
205	cw->curx = cw->cury = 0;
206	home();
207	cl_end();
208    }
209    ttyDisplay->toplin = 0;
210    ttyDisplay->inmore = 0;
211}
212
213void
214update_topl(bp)
215	register const char *bp;
216{
217	register char *tl, *otl;
218	register int n0;
219	int notdied = 1;
220	struct WinDesc *cw = wins[WIN_MESSAGE];
221
222	/* If there is room on the line, print message on same line */
223	/* But messages like "You die..." deserve their own line */
224	n0 = strlen(bp);
225	if ((ttyDisplay->toplin == 1 || (cw->flags & WIN_STOP)) &&
226	    cw->cury == 0 &&
227	    n0 + (int)strlen(toplines) + 3 < CO-8 &&  /* room for --More-- */
228	    (notdied = strncmp(bp, "You die", 7))) {
229		Strcat(toplines, "  ");
230		Strcat(toplines, bp);
231		cw->curx += 2;
232		if(!(cw->flags & WIN_STOP))
233		    addtopl(bp);
234		return;
235	} else if (!(cw->flags & WIN_STOP)) {
236	    if(ttyDisplay->toplin == 1) more();
237	    else if(cw->cury) {	/* for when flags.toplin == 2 && cury > 1 */
238		docorner(1, cw->cury+1); /* reset cury = 0 if redraw screen */
239		cw->curx = cw->cury = 0;/* from home--cls() & docorner(1,n) */
240	    }
241	}
242	remember_topl();
243	(void) strncpy(toplines, bp, TBUFSZ);
244	toplines[TBUFSZ - 1] = 0;
245
246	for(tl = toplines; n0 >= CO; ){
247	    otl = tl;
248	    for(tl+=CO-1; tl != otl && !isspace(*tl); --tl) ;
249	    if(tl == otl) {
250		/* Eek!  A huge token.  Try splitting after it. */
251		tl = index(otl, ' ');
252		if (!tl) break;    /* No choice but to spit it out whole. */
253	    }
254	    *tl++ = '\n';
255	    n0 = strlen(tl);
256	}
257	if(!notdied) cw->flags &= ~WIN_STOP;
258	if(!(cw->flags & WIN_STOP)) redotoplin(toplines);
259}
260
261STATIC_OVL
262void
263topl_putsym(c)
264    char c;
265{
266    register struct WinDesc *cw = wins[WIN_MESSAGE];
267
268    if(cw == (struct WinDesc *) 0) panic("Putsym window MESSAGE nonexistant");
269
270    switch(c) {
271    case '\b':
272	if(ttyDisplay->curx == 0 && ttyDisplay->cury > 0)
273	    tty_curs(BASE_WINDOW, CO, (int)ttyDisplay->cury-1);
274	backsp();
275	ttyDisplay->curx--;
276	cw->curx = ttyDisplay->curx;
277	return;
278    case '\n':
279	cl_end();
280	ttyDisplay->curx = 0;
281	ttyDisplay->cury++;
282	cw->cury = ttyDisplay->cury;
283#ifdef WIN32CON
284    (void) putchar(c);
285#endif
286	break;
287    default:
288	if(ttyDisplay->curx == CO-1)
289	    topl_putsym('\n'); /* 1 <= curx <= CO; avoid CO */
290#ifdef WIN32CON
291    (void) putchar(c);
292#endif
293	ttyDisplay->curx++;
294    }
295    cw->curx = ttyDisplay->curx;
296    if(cw->curx == 0) cl_end();
297#ifndef WIN32CON
298    (void) putchar(c);
299#endif
300}
301
302void
303putsyms(str)
304    const char *str;
305{
306    while(*str)
307	topl_putsym(*str++);
308}
309
310STATIC_OVL void
311removetopl(n)
312register int n;
313{
314    /* assume addtopl() has been done, so ttyDisplay->toplin is already set */
315    while (n-- > 0) putsyms("\b \b");
316}
317
318extern char erase_char;		/* from xxxtty.c; don't need kill_char */
319
320char
321tty_yn_function(query,resp, def)
322const char *query,*resp;
323char def;
324/*
325 *   Generic yes/no function. 'def' is the default (returned by space or
326 *   return; 'esc' returns 'q', or 'n', or the default, depending on
327 *   what's in the string. The 'query' string is printed before the user
328 *   is asked about the string.
329 *   If resp is NULL, any single character is accepted and returned.
330 *   If not-NULL, only characters in it are allowed (exceptions:  the
331 *   quitchars are always allowed, and if it contains '#' then digits
332 *   are allowed); if it includes an <esc>, anything beyond that won't
333 *   be shown in the prompt to the user but will be acceptable as input.
334 */
335{
336	register char q;
337	char rtmp[40];
338	boolean digit_ok, allow_num;
339	struct WinDesc *cw = wins[WIN_MESSAGE];
340	boolean doprev = 0;
341	char prompt[QBUFSZ];
342
343	if(ttyDisplay->toplin == 1 && !(cw->flags & WIN_STOP)) more();
344	cw->flags &= ~WIN_STOP;
345	ttyDisplay->toplin = 3; /* special prompt state */
346	ttyDisplay->inread++;
347	if (resp) {
348	    char *rb, respbuf[QBUFSZ];
349
350	    allow_num = (index(resp, '#') != 0);
351	    Strcpy(respbuf, resp);
352	    /* any acceptable responses that follow <esc> aren't displayed */
353	    if ((rb = index(respbuf, '\033')) != 0) *rb = '\0';
354	    Sprintf(prompt, "%s [%s] ", query, respbuf);
355	    if (def) Sprintf(eos(prompt), "(%c) ", def);
356	    pline("%s", prompt);
357	} else {
358	    pline("%s ", query);
359	    q = readchar();
360	    goto clean_up;
361	}
362
363	do {	/* loop until we get valid input */
364	    q = lowc(readchar());
365	    if (q == '\020') { /* ctrl-P */
366		if (iflags.prevmsg_window != 's') {
367		    int sav = ttyDisplay->inread;
368		    ttyDisplay->inread = 0;
369		    (void) tty_doprev_message();
370		    ttyDisplay->inread = sav;
371		    tty_clear_nhwindow(WIN_MESSAGE);
372		    cw->maxcol = cw->maxrow;
373		    addtopl(prompt);
374		} else {
375		    if(!doprev)
376			(void) tty_doprev_message(); /* need two initially */
377		    (void) tty_doprev_message();
378		    doprev = 1;
379		}
380		q = '\0';	/* force another loop iteration */
381		continue;
382	    } else if (doprev) {
383		/* BUG[?]: this probably ought to check whether the
384		   character which has just been read is an acceptable
385		   response; if so, skip the reprompt and use it. */
386		tty_clear_nhwindow(WIN_MESSAGE);
387		cw->maxcol = cw->maxrow;
388		doprev = 0;
389		addtopl(prompt);
390		q = '\0';	/* force another loop iteration */
391		continue;
392	    }
393	    digit_ok = allow_num && digit(q);
394	    if (q == '\033') {
395		if (index(resp, 'q'))
396		    q = 'q';
397		else if (index(resp, 'n'))
398		    q = 'n';
399		else
400		    q = def;
401		break;
402	    } else if (index(quitchars, q)) {
403		q = def;
404		break;
405	    }
406	    if (!index(resp, q) && !digit_ok) {
407		tty_nhbell();
408		q = (char)0;
409	    } else if (q == '#' || digit_ok) {
410		char z, digit_string[2];
411		int n_len = 0;
412		long value = 0;
413		addtopl("#"),  n_len++;
414		digit_string[1] = '\0';
415		if (q != '#') {
416		    digit_string[0] = q;
417		    addtopl(digit_string),  n_len++;
418		    value = q - '0';
419		    q = '#';
420		}
421		do {	/* loop until we get a non-digit */
422		    z = lowc(readchar());
423		    if (digit(z)) {
424			value = (10 * value) + (z - '0');
425			if (value < 0) break;	/* overflow: try again */
426			digit_string[0] = z;
427			addtopl(digit_string),  n_len++;
428		    } else if (z == 'y' || index(quitchars, z)) {
429			if (z == '\033')  value = -1;	/* abort */
430			z = '\n';	/* break */
431		    } else if (z == erase_char || z == '\b') {
432			if (n_len <= 1) { value = -1;  break; }
433			else { value /= 10;  removetopl(1),  n_len--; }
434		    } else {
435			value = -1;	/* abort */
436			tty_nhbell();
437			break;
438		    }
439		} while (z != '\n');
440		if (value > 0) yn_number = value;
441		else if (value == 0) q = 'n';		/* 0 => "no" */
442		else {	/* remove number from top line, then try again */
443			removetopl(n_len),  n_len = 0;
444			q = '\0';
445		}
446	    }
447	} while(!q);
448
449	if (q != '#') {
450		Sprintf(rtmp, "%c", q);
451		addtopl(rtmp);
452	}
453    clean_up:
454	ttyDisplay->inread--;
455	ttyDisplay->toplin = 2;
456	if (ttyDisplay->intr) ttyDisplay->intr--;
457	if(wins[WIN_MESSAGE]->cury)
458	    tty_clear_nhwindow(WIN_MESSAGE);
459
460	return q;
461}
462
463#endif /* OVL2 */
464
465#endif /* TTY_GRAPHICS */
466
467/*topl.c*/
468