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