getkey.cpp 13.9 KB
Newer Older
1
2
3
4
5
6
7
8
/* Synchronet single-key console functions */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
9
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
rswindell's avatar
rswindell committed
37
#include "telnet.h"	// TELNET_GA
38
39
40
41
42
43
44
45
46

/****************************************************************************/
/* Waits for remote or local user to hit a key. Inactivity timer is checked */
/* and hangs up if inactive for 4 minutes. Returns key hit, or uppercase of */
/* key hit if mode&K_UPPER or key out of KEY BUFFER. Does not print key.    */
/* Called from functions all over the place.                                */
/****************************************************************************/
char sbbs_t::getkey(long mode)
{
47
	uchar	ch,coldkey,c=0,spin=sbbs_random(5);
48
	time_t	last_telnet_cmd=0;
rswindell's avatar
rswindell committed
49
	long	term = term_supports();
50

51
52
53
	if(online==ON_REMOTE && !input_thread_running)
		online=FALSE;
	if(!online) {
54
		YIELD();	// just in case someone is looping on getkey() when they shouldn't
55
		return(0);
56
	}
57
	sys_status&=~SS_ABORT;
58
	if((sys_status&SS_USERON || action==NODE_DFLT) && !(mode&(K_GETSTR|K_NOSPIN)))
59
60
61
62
63
		mode|=(useron.misc&SPIN);
	lncntr=0;
	timeout=time(NULL);
	if(mode&K_SPIN)
		outchar(' ');
64

65
66
67
	do {
		if(sys_status&SS_ABORT) {
			if(mode&K_SPIN) /* back space once if on spinning cursor */
68
				backspace();
69
70
			return(0); 
		}
71

72
		if(mode&K_SPIN) {
rswindell's avatar
rswindell committed
73
			if(term&NO_EXASCII) {
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
				switch(c++) {
					case 0:
						outchar(BS);
						outchar('|');
						break;
					case 1:
						outchar(BS);
						outchar('/');
						break;
					case 2:
						outchar(BS);
						outchar('-');
						break;
					case 3:
						outchar(BS);
						outchar('\\');
rswindell's avatar
rswindell committed
90
						c=0;
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
						break;
				}
			} else {
				switch(spin) {
					case 0:
						switch(c++) {
							case 0:
								outchar(BS);
								outchar('');
								break;
							case 1:
								outchar(BS);
								outchar('/');
								break;
							case 2:
								outchar(BS);
								outchar('');
								break;
							case 3:
								outchar(BS);
								outchar('\\');
								c=0;
								break;
						}
						break;
					case 1:
						switch(c++) {
							case 0:
								outchar(BS);
								outchar('');
								break;
							case 1:
								outchar(BS);
								outchar('');
								break;
							case 2:
								outchar(BS);
								outchar('');
								break;
							case 3:
								outchar(BS);
								outchar('');
								break;
							case 4:
								outchar(BS);
								outchar('');
								break;
							case 5:
								outchar(BS);
								outchar('');
								c=0;
								break;
						}
						break;
					case 2:
						switch(c++) {
							case 0:
								outchar(BS);
								outchar('-');
								break;
							case 1:
								outchar(BS);
								outchar('=');
								break;
							case 2:
								outchar(BS);
								outchar('');
								break;
							case 3:
								outchar(BS);
								outchar('=');
								c=0;
								break;
						}
						break;
					case 3:
						switch(c++) {
							case 0:
								outchar(BS);
								outchar('');
								break;
							case 1:
								outchar(BS);
								outchar('');
								break;
							case 2:
								outchar(BS);
								outchar('');
								break;
							case 3:
								outchar(BS);
								outchar('');
								c=0;
								break;
						}
						break;
					case 4:
						switch(c++) {
							case 0:
								outchar(BS);
								outchar('');
								break;
							case 1:
								outchar(BS);
								outchar('');
								break;
							case 2:
								outchar(BS);
								outchar('');
								break;
							case 3:
								outchar(BS);
								outchar('');
								c=0;
								break;
						}
						break; 
				}
209
			}
210
		}
211
		ch=inkey(mode,mode&K_SPIN ? 250:1000);
212
213
214
215
216
217
218
219
220
221
222
		if(sys_status&SS_ABORT)
			return(0);
		now=time(NULL);
		if(ch) {
			if(mode&K_NUMBER && isprint(ch) && !isdigit(ch))
				continue;
			if(mode&K_ALPHA && isprint(ch) && !isalpha(ch))
				continue;
			if(mode&K_NOEXASC && ch&0x80)
				continue;
			if(mode&K_SPIN)
223
				backspace();
224
			if(mode&K_COLD && ch>' ' && useron.misc&COLDKEYS) {
225
226
227
228
				if(mode&K_UPPER)
					outchar(toupper(ch));
				else
					outchar(ch);
229
230
				while((coldkey=inkey(mode,1000))==0 && online && !(sys_status&SS_ABORT))
					;
231
				backspace();
232
				if(coldkey==BS || coldkey==DEL)
233
					continue;
234
				if(coldkey>' ')
235
236
					ungetkey(coldkey); 
			}
237
238
			if(mode&K_UPPER)
				return(toupper(ch));
239
240
			return(ch); 
		}
241
		if(sys_status&SS_USERON && !(sys_status&SS_LCHAT)) gettimeleft();
rswindell's avatar
rswindell committed
242
		else if(online && now-answertime>SEC_LOGON && !(sys_status&SS_LCHAT)) {
243
244
245
			console&=~(CON_R_ECHOX|CON_L_ECHOX);
			console|=(CON_R_ECHO|CON_L_ECHO);
			bputs(text[TakenTooLongToLogon]);
246
247
			hangup(); 
		}
248
249
250
251
252
253
254
		if(sys_status&SS_USERON && online && (timeleft/60)<(5-timeleft_warn)
			&& !SYSOP && !(sys_status&SS_LCHAT)) {
			timeleft_warn=5-(timeleft/60);
			SAVELINE;
			attr(LIGHTGRAY);
			bprintf(text[OnlyXminutesLeft]
				,((ushort)timeleft/60)+1,(timeleft/60) ? "s" : nulstr);
255
256
			RESTORELINE; 
		}
257

258
		if(!(startup->options&BBS_OPT_NO_TELNET_GA)
259
			&& now!=last_telnet_cmd && now-timeout>=60 && !((now-timeout)%60)) {
260
261
			// Let's make sure the socket is up
			// Sending will trigger a socket d/c detection
262
			send_telnet_cmd(TELNET_GA,0);
263
264
265
			last_telnet_cmd=now;
		}
			
266
		if(online==ON_REMOTE && !(console&CON_NO_INACT)
267
268
			&& (now-timeout >= cfg.sec_warn || now-timeout >= cfg.sec_hangup)) {
			if(sys_status&SS_USERON && cfg.sec_warn < cfg.sec_hangup) {
269
				SAVELINE;
270
271
				bputs(text[AreYouThere]); 
			}
272
273
			else
				bputs("\7\7");
274
			while(!inkey(K_NONE,100) && online && now-timeout < cfg.sec_hangup) {
275
				now=time(NULL);
276
277
278
279
280
			}
			if(now-timeout >= cfg.sec_hangup) {
				if(online==ON_REMOTE) {
					console|=CON_R_ECHO;
					console&=~CON_R_ECHOX; 
281
				}
282
283
284
285
				bputs(text[CallBackWhenYoureThere]);
				logline(LOG_NOTICE,nulstr,"Inactive");
				hangup();
				return(0); 
286
			}
287
			if(sys_status&SS_USERON) {
288
				bputs("\r\1n\1>");
289
290
291
292
				RESTORELINE; 
			}
			timeout=now; 
		}
293

294
295
	} while(online);

296
297
298
299
300
	return(0);
}


/****************************************************************************/
301
/* Outputs a string highlighting characters preceded by a tilde             */
302
/****************************************************************************/
303
void sbbs_t::mnemonics(const char *str)
304
{
rswindell's avatar
rswindell committed
305
    const char *ctrl_a_codes;
306
307
308
309
310
    long l;

	if(!strchr(str,'~')) {
		mnestr=str;
		bputs(str);
311
312
		return; 
	}
313
314
	ctrl_a_codes=strchr(str,1);
	if(!ctrl_a_codes) {
315
		if(str[0]=='@' && str[strlen(str)-1]=='@' && !strchr(str,' ')) {
316
317
			mnestr=str;
			bputs(str);
318
319
320
321
			return; 
		}
		attr(cfg.color[clr_mnelow]); 
	}
322
	l=0L;
rswindell's avatar
rswindell committed
323
	long term = term_supports();
324
	while(str[l]) {
325
		if(str[l]=='~' && str[l+1]!=0) {
rswindell's avatar
rswindell committed
326
			if(!(term&(ANSI|PETSCII)))
327
328
329
330
331
332
				outchar('(');
			l++;
			if(!ctrl_a_codes)
				attr(cfg.color[clr_mnehigh]);
			outchar(str[l]);
			l++;
rswindell's avatar
rswindell committed
333
			if(!(term&(ANSI|PETSCII)))
334
335
				outchar(')');
			if(!ctrl_a_codes)
336
337
				attr(cfg.color[clr_mnelow]); 
		}
338
		else {
rswindell's avatar
rswindell committed
339
340
			if(str[l]==CTRL_A && str[l+1]!=0) {
				l++;
341
342
				if(str[l] == 'Z')	/* EOF (uppercase 'Z') */
					break;
rswindell's avatar
rswindell committed
343
				ctrl_a(str[l++]);
344
345
346
347
348
349
350
351
352
353
			} else {
				if(str[l] == '@') {
					int i = show_atcode(str + l);
					if(i) {
						l += i;
						continue;
					}
				}
				outchar(str[l++]);
			}
354
355
		} 
	}
356
357
358
359
360
361
	if(!ctrl_a_codes)
		attr(cfg.color[clr_mnecmd]);
}

/****************************************************************************/
/* Prompts user for Y or N (yes or no) and CR is interpreted as a Y         */
rswindell's avatar
rswindell committed
362
/* Returns true for Yes or false for No                                     */
363
364
/* Called from quite a few places                                           */
/****************************************************************************/
365
bool sbbs_t::yesno(const char *str, long mode)
366
367
368
{
    char ch;

rswindell's avatar
rswindell committed
369
370
	if(*str == 0)
		return true;
rswindell's avatar
rswindell committed
371
	SAFECOPY(question,str);
372
	SYNC;
373
	bprintf(mode, text[YesNoQuestion], str);
374
375
	while(online) {
		if(sys_status&SS_ABORT)
376
			ch=text[YNQP][1];
377
378
		else
			ch=getkey(K_UPPER|K_COLD);
379
		if(ch==text[YNQP][0] || ch==CR) {
380
			if(bputs(text[Yes], mode) && !(mode&P_NOCRLF))
381
382
				CRLF;
			lncntr=0;
383
384
			return(true); 
		}
385
		if(ch==text[YNQP][1]) {
386
			if(bputs(text[No], mode) && !(mode&P_NOCRLF))
387
388
				CRLF;
			lncntr=0;
389
390
391
			return(false); 
		} 
	}
392
393
394
395
396
	return(true);
}

/****************************************************************************/
/* Prompts user for N or Y (no or yes) and CR is interpreted as a N         */
rswindell's avatar
rswindell committed
397
/* Returns true for No or false for Yes                                     */
398
/****************************************************************************/
399
bool sbbs_t::noyes(const char *str, long mode)
400
401
402
{
    char ch;

rswindell's avatar
rswindell committed
403
	if(*str == 0)
404
		return true;
rswindell's avatar
rswindell committed
405
	SAFECOPY(question,str);
406
	SYNC;
407
	bprintf(mode, text[NoYesQuestion], str);
408
409
	while(online) {
		if(sys_status&SS_ABORT)
410
			ch=text[YNQP][1];
411
412
		else
			ch=getkey(K_UPPER|K_COLD);
413
		if(ch==text[YNQP][1] || ch==CR) {
414
			if(bputs(text[No], mode) && !(mode&P_NOCRLF))
415
416
				CRLF;
			lncntr=0;
417
418
			return(true); 
		}
419
		if(ch==text[YNQP][0]) {
420
			if(bputs(text[Yes], mode) && !(mode&P_NOCRLF))
421
422
				CRLF;
			lncntr=0;
423
424
425
			return(false); 
		} 
	}
426
427
428
429
	return(true);
}

/****************************************************************************/
430
431
432
433
/* Waits for remote or local user to hit a key among 'keys'.				*/
/* If 'keys' is NULL, *any* non-numeric key is valid input.					*/
/* 'max' is non-zero, allow that a decimal number input up to that size		*/
/* and return the value OR'd with 0x80000000.								*/
434
/* default mode value is K_UPPER											*/
435
/****************************************************************************/
436
long sbbs_t::getkeys(const char *keys, ulong max, long mode)
437
{
438
	char	str[81];
439
	uchar	ch,n=0,c=0;
440
	ulong	i=0;
441

442
443
444
	if(keys != NULL) {
		SAFECOPY(str,keys);
	}
445
	while(online) {
446
		ch=getkey(mode);
447
448
449
		if(max && ch>0x7f)  /* extended ascii chars are digits to isdigit() */
			continue;
		if(sys_status&SS_ABORT) {   /* return -1 if Ctrl-C hit */
450
451
452
453
			if(!(mode&(K_NOECHO|K_NOCRLF))) {
				attr(LIGHTGRAY);
				CRLF;
			}
454
			lncntr=0;
455
456
			return(-1); 
		}
457
458
		if(ch && !n && ((keys == NULL && !isdigit(ch)) || (strchr(str,ch)))) {  /* return character if in string */
			if(ch > ' ') {
459
460
				if(!(mode&K_NOECHO))
					outchar(ch);
461
462
463
464
465
466
467
				if(useron.misc&COLDKEYS) {
					while(online && !(sys_status&SS_ABORT)) {
						c=getkey(0);
						if(c==CR || c==BS || c==DEL)
							break; 
					}
					if(sys_status&SS_ABORT) {
468
469
470
						if(!(mode&(K_NOECHO|K_NOCRLF))) {
							CRLF;
						}
471
472
473
						return(-1); 
					}
					if(c==BS || c==DEL) {
474
475
						if(!(mode&K_NOECHO))
							backspace();
476
477
						continue; 
					} 
478
				}
479
480
481
482
				if(!(mode&(K_NOECHO|K_NOCRLF))) {
					attr(LIGHTGRAY);
					CRLF;
				}
483
				lncntr=0;
484
485
486
			}
			return(ch); 
		}
487
		if(ch==CR && max) {             /* return 0 if no number */
488
489
490
491
			if(!(mode&(K_NOECHO|K_NOCRLF))) {
				attr(LIGHTGRAY);
				CRLF;
			}
492
493
494
			lncntr=0;
			if(n)
				return(i|0x80000000L);		 /* return number plus high bit */
495
496
497
			return(0); 
		}
		if((ch==BS || ch==DEL) && n) {
498
499
			if(!(mode&K_NOECHO))
				backspace();
500
			i/=10;
501
502
			n--; 
		}
503
504
505
506
		else if(max && isdigit(ch) && (i*10)+(ch&0xf)<=max && (ch!='0' || n)) {
			i*=10;
			n++;
			i+=ch&0xf;
507
508
			if(!(mode&K_NOECHO))	
				outchar(ch);
509
			if(i*10>max && !(useron.misc&COLDKEYS)) {
510
511
512
513
				if(!(mode&(K_NOECHO|K_NOCRLF))) {
					attr(LIGHTGRAY);
					CRLF;
				}
514
				lncntr=0;
515
516
517
518
				return(i|0x80000000L); 
			} 
		} 
	}
519
520
521
522
523
524
525
526
527
	return(-1);
}

/****************************************************************************/
/* Prints PAUSE message and waits for a key stoke                           */
/****************************************************************************/
void sbbs_t::pause()
{
	char	ch;
rswindell's avatar
rswindell committed
528
	uint	tempattrs=curatr; /* was lclatr(-1) */
529
	long	l=K_UPPER;
530
	size_t	len;
531

532
 	if(sys_status&SS_ABORT)
533
534
535
536
537
		return;
	lncntr=0;
	if(online==ON_REMOTE)
		rioctl(IOFI);
	bputs(text[Pause]);
538
	len = bstrlen(text[Pause]);
deuce's avatar
deuce committed
539
	if(sys_status&SS_USERON && !(useron.misc&(HTML|WIP|NOPAUSESPIN))
540
541
542
		&& !(cfg.node_misc&NM_NOPAUSESPIN))
		l|=K_SPIN;
	ch=getkey(l);
543
	if(ch==text[YNQP][1] || ch==text[YNQP][2])
544
545
546
547
		sys_status|=SS_ABORT;
	else if(ch==LF)	// down arrow == display one more line
		lncntr=rows-2;
	if(text[Pause][0]!='@')
548
		backspace(len);
549
550
551
552
553
554
555
556
557
558
	getnodedat(cfg.node_num,&thisnode,0);
	nodesync();
	attr(tempattrs);
}

/****************************************************************************/
/* Puts a character into the input buffer                                   */
/****************************************************************************/
void sbbs_t::ungetkey(char ch)
{
559
#if 0	/* this way breaks ansi_getxy() */
560
	RingBufWrite(&inbuf,(uchar*)&ch,sizeof(uchar));
561
562
563
564
565
#else
	keybuf[keybuftop++]=ch;   
	if(keybuftop==KEY_BUFSIZE)   
		keybuftop=0; 
#endif
566
}