1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: getkey.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
3 #endif
4 
5 /*
6  * ========================================================================
7  * Copyright 2006-2007 University of Washington
8  * Copyright 2013-2021 Eduardo Chappa
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * ========================================================================
17  */
18 
19 #include <system.h>
20 #include <general.h>
21 
22 #include "../estruct.h"
23 #include "../mode.h"
24 #include "../pico.h"
25 #include "../edef.h"
26 #include "../efunc.h"
27 #include "../keydefs.h"
28 
29 #include "tty.h"
30 #include "getkey.h"
31 #include "read.h"
32 #include "mouse.h"
33 
34 #ifdef _WINDOWS
35 #include "mswin.h"
36 static int	MapMSKEYtoPK(int c);
37 #endif /* _WINDOWS */
38 
39 
40 /* internal declarations */
41 static int timeo = 0;
42 
43 /* these next two are declared in pith/conf.h */
44 int
set_input_timeout(int t)45 set_input_timeout(int t)
46 {
47     int oldtimeo = timeo;
48 
49     timeo = t;
50     return(oldtimeo);
51 }
52 
53 int
get_input_timeout(void)54 get_input_timeout(void)
55 {
56     return(timeo);
57 }
58 
59 
60 #ifndef _WINDOWS
61 
62 
63 /* internal prototypes */
64 void		bail(void);
65 int		ReadyForKey(int);
66 
67 
68 
69 void
bail(void)70 bail(void)
71 {
72     sleep(30);				/* see if os receives SIGHUP */
73     kill(getpid(), SIGHUP);			/* eof or bad error */
74 }
75 
76 
77 #if	TYPEAH
78 /*
79  * typahead - Check to see if any characters are already in the
80  *	      keyboard buffer
81  */
82 int
typahead(void)83 typahead(void)
84 {
85     int x;	/* holds # of pending chars */
86 
87     return((ioctl(0,FIONREAD,&x) < 0) ? 0 : x);
88 }
89 #endif /* TYPEAH */
90 
91 
92 
93 /*
94  * ReadyForKey - return true if there's no timeout or we're told input
95  *		 is available...
96  */
97 int
ReadyForKey(int timeout)98 ReadyForKey(int timeout)
99 {
100     switch(input_ready(timeout)){
101       case READY_TO_READ:
102 	return(1);
103 	break;
104 
105       case NO_OP_COMMAND:
106       case NO_OP_IDLE:
107       case READ_INTR:
108 	return(0);
109 
110       case BAIL_OUT:
111       case PANIC_NOW:
112 	emlwwrite(_("Problem reading from keyboard!"), NULL);
113 	kill(getpid(), SIGHUP);	/* Bomb out (saving our work)! */
114 	/* no return */
115     }
116 
117     /* can't happen */
118     return(0);
119 }
120 
121 
122 
123 /*
124  * GetKey - Read in a key.
125  * Do the standard keyboard preprocessing. Convert the keys to the internal
126  * character set.  Resolves escape sequences and returns no-op if global
127  * timeout value exceeded.
128  */
129 UCS
GetKey(void)130 GetKey(void)
131 {
132     UCS ch, status, cc;
133 
134     if(!ReadyForKey(FUDGE-5))
135       return(NODATA);
136 
137     switch(status = kbseq(simple_ttgetc, NULL, bail, input_cs, &ch)){
138       case 0: 	/* regular character */
139 	break;
140 
141       case KEY_DOUBLE_ESC:
142 	/*
143 	 * Special hack to get around comm devices eating control characters.
144 	 */
145 	if(!ReadyForKey(5))
146 	  return(BADESC);		/* user typed ESC ESC, then stopped */
147 	else
148 	    switch(status = kbseq(simple_ttgetc, NULL, bail, input_cs, &ch)){
149 	      case  KEY_UP		:
150 	      case  KEY_DOWN		:
151 	      case  KEY_RIGHT		:
152 	      case  KEY_LEFT		:
153 	      case  KEY_PGUP		:
154 	      case  KEY_PGDN		:
155 	      case  KEY_HOME		:
156 	      case  KEY_END		:
157 	      case  KEY_DEL		:
158 	      case F1  :
159 	      case F2  :
160 	      case F3  :
161 	      case F4  :
162 	      case F5  :
163 	      case F6  :
164 	      case F7  :
165 	      case F8  :
166 	      case F9  :
167 	      case F10 :
168 	      case F11 :
169 	      case F12 :
170 		return(CTRL | status);
171 		break;
172 
173 	      case 0: 	/* regular character */
174 		break;
175 
176 	      default:				/* punt the whole thing	*/
177 		(*term.t_beep)();
178 		return(BADESC);
179 		break;
180 	    }
181 
182 	ch &= 0x7f;
183 	if(isdigit((unsigned char)ch)){
184 	    int n = 0, i = ch - '0';
185 
186 	    if(i < 0 || i > 2)
187 	      return(BADESC);		/* bogus literal char value */
188 
189 	    while(n++ < 2){
190 		if(!ReadyForKey(5)
191 		   || (!isdigit((unsigned char) (ch =
192 				   (*term.t_getchar)(NODATA, NULL, bail)))
193 		       || (n == 1 && i == 2 && ch > '5')
194 		       || (n == 2 && i == 25 && ch > '5'))){
195 		    return(BADESC);
196 		}
197 
198 		i = (i * 10) + (ch - '0');
199 	    }
200 
201 	    ch = i;
202 	}
203 	else{
204 	    if(islower((unsigned char)ch))	/* canonicalize if alpha */
205 	      ch = toupper((unsigned char)ch);
206 
207 	    return((isalpha((unsigned char)ch) || ch == '@'
208 		    || (ch >= '[' && ch <= '_'))
209 		    ? (CTRL | ch) : ((ch == ' ') ? (CTRL | '@') : ch));
210 	}
211 
212 	break;
213 
214 #ifdef MOUSE
215       case KEY_XTERM_MOUSE:
216 	{
217 	    /*
218 	     * Special hack to get mouse events from an xterm.
219 	     * Get the details, then pass it past the keymenu event
220 	     * handler, and then to the installed handler if there
221 	     * is one...
222 	     */
223 	    static int    down = 0;
224 	    int           x, y, button;
225 	    unsigned long cmd;
226 
227 	    button = (*term.t_getchar)(NODATA, NULL, bail) & 0x03;
228 
229 	    x = (*term.t_getchar)(NODATA, NULL, bail) - '!';
230 	    y = (*term.t_getchar)(NODATA, NULL, bail) - '!';
231 
232 	    if(button == 0){
233 		down = 1;
234 		if(checkmouse(&cmd, 1, x, y))
235 		  return((UCS) cmd);
236 	    }
237 	    else if(down && button == 3){
238 		down = 0;
239 		if(checkmouse(&cmd, 0, x, y))
240 		  return((UCS) cmd);
241 	    }
242 
243 	    return(NODATA);
244 	}
245 
246 	break;
247 #endif /* MOUSE */
248 
249       case  KEY_UP		:
250       case  KEY_DOWN		:
251       case  KEY_RIGHT		:
252       case  KEY_LEFT		:
253       case  KEY_PGUP		:
254       case  KEY_PGDN		:
255       case  KEY_HOME		:
256       case  KEY_END		:
257       case  KEY_DEL		:
258       case F1  :
259       case F2  :
260       case F3  :
261       case F4  :
262       case F5  :
263       case F6  :
264       case F7  :
265       case F8  :
266       case F9  :
267       case F10 :
268       case F11 :
269       case F12 :
270 	return(status);
271 
272       case  CTRL_KEY_UP		:
273 	return(CTRL | KEY_UP);
274       case  CTRL_KEY_DOWN	:
275 	return(CTRL | KEY_DOWN);
276       case  CTRL_KEY_RIGHT	:
277 	return(CTRL | KEY_RIGHT);
278       case  CTRL_KEY_LEFT	:
279 	return(CTRL | KEY_LEFT);
280 
281       case KEY_SWALLOW_Z:
282 	status = BADESC;
283       case KEY_SWAL_UP:
284       case KEY_SWAL_DOWN:
285       case KEY_SWAL_LEFT:
286       case KEY_SWAL_RIGHT:
287 	do
288 	  if(!ReadyForKey(2)){
289 	      status = BADESC;
290 	      break;
291 	  }
292 	while(!strchr("~qz", (*term.t_getchar)(NODATA, NULL, bail)));
293 
294 	return((status == BADESC)
295 		? status
296 		: status - (KEY_SWAL_UP - KEY_UP));
297 	break;
298 
299       case KEY_KERMIT:
300 	do{
301 	    cc = ch;
302 	    if(!ReadyForKey(2))
303 	      return(BADESC);
304 	    else
305 	      ch = (*term.t_getchar)(NODATA, NULL, bail) & 0x7f;
306 	}while(cc != '\033' && ch != '\\');
307 
308 	ch = NODATA;
309 	break;
310 
311       case BADESC:
312 	(*term.t_beep)();
313 	return(status);
314 
315       default:				/* punt the whole thing	*/
316 	(*term.t_beep)();
317 	break;
318     }
319 
320     if (ch >= 0x00 && ch <= 0x1F)       /* C0 control -> C-     */
321       ch = CTRL | (ch+'@');
322 
323     return(ch);
324 }
325 
326 
327 /*
328  * kbseq - looks at an escape sequence coming from the keyboard and
329  *         compares it to a trie of known keyboard escape sequences, and
330  *         returns the function bound to the escape sequence.
331  *
332  *     Args: getcfunc     -- Function to get a single character from stdin,
333  *                           called with the next two arguments to this
334  *                           function as its arguments.
335  *           recorder     -- If non-NULL, function used to record keystroke.
336  *           bail_handler -- Function used to bail out on read error.
337  *           c            -- Pointer to returned character.
338  *
339  *  Returns: BADESC
340  *           The escaped function.
341  *           0 if a regular char with char stuffed in location c.
342  */
343 UCS
kbseq(int (* getcfunc)(int (* recorder)(int),void (* bail_handler)(void)),int (* recorder)(int),void (* bail_handler)(void),void * data,UCS * ch)344 kbseq(int (*getcfunc)(int (*recorder)(int ), void (*bail_handler)(void )),
345       int (*recorder)(int),
346       void (*bail_handler)(void),
347       void *data,
348       UCS *ch)
349 {
350     unsigned char c;
351     int      first = 1;
352     KBESC_T *current;
353 
354     current = kbesc;
355     if(current == NULL)				/* bag it */
356       return(BADESC);
357 
358     while(1){
359 	c = (*getcfunc)(recorder, bail_handler);
360 
361 	while(current->value != c){
362 	    if(current->left == NULL){		/* NO MATCH */
363 		if(first){
364 		    unsigned long octets_so_far, remaining_octets;
365 		    unsigned char *inputp;
366 		    UCS ucs;
367 		    unsigned char inputbuf[20];
368 
369 		    /*
370 		     * Regular character.
371 		     * Read enough bytes to make up a character and convert it to UCS-4.
372 		     */
373 		    memset(inputbuf, 0, sizeof(inputbuf));
374 		    inputbuf[0] = c;
375 		    octets_so_far = 1;
376 		    for(;;){
377 			remaining_octets = octets_so_far;
378 			inputp = inputbuf;
379 			ucs = mbtow(data, &inputp, &remaining_octets);
380 			switch(ucs){
381 			  case CCONV_BADCHAR:
382 			    /*
383 			     * Not really a BADESC but that ought to
384 			     * be sufficient. We can add another type if
385 			     * we need to.
386 			     */
387 			    return(BADESC);
388 
389 			  case CCONV_NEEDMORE:
390 			    if(octets_so_far >= sizeof(inputbuf))
391 			      return(BADESC);
392 
393 			    c = (*getcfunc)(recorder, bail_handler);
394 			    inputbuf[octets_so_far++] = c;
395 			    break;
396 
397 			  default:
398 			    /* got a good UCS-4 character */
399 			    *ch = ucs;
400 			    return(0);
401 			}
402 		    }
403 
404 		    /* NOTREACHED */
405 		    return(0);
406 		}
407 		else
408 		  return(BADESC);
409 	    }
410 	    current = current->left;
411 	}
412 
413 	if(current->down == NULL)		/* match!!!*/
414 	  return(current->func);
415 	else
416 	  current = current->down;
417 
418 	first = 0;
419     }
420 }
421 
422 
423 #define	newnode()	(KBESC_T *)malloc(sizeof(KBESC_T))
424 
425 /*
426  * kpinsert - insert a keystroke escape sequence into the global search
427  *	      structure.
428  */
429 void
kpinsert(char * kstr,int kval,int termcap_wins)430 kpinsert(char *kstr, int kval, int termcap_wins)
431 {
432     register	char	*buf;
433     register	KBESC_T *temp;
434     register	KBESC_T *trail;
435 
436     if(kstr == NULL)
437       return;
438 
439     /*
440      * Don't allow escape sequences that don't start with ESC unless
441      * termcap_wins.  This is to protect against mistakes in termcap files.
442      */
443     if(!termcap_wins && *kstr != '\033')
444       return;
445 
446     temp = trail = kbesc;
447     buf = kstr;
448 
449     for(;;){
450 	if(temp == NULL){
451 	    temp = newnode();
452 	    temp->value = *buf;
453 	    temp->func = 0;
454 	    temp->left = NULL;
455 	    temp->down = NULL;
456 	    if(kbesc == NULL)
457 	      kbesc = temp;
458 	    else
459 	      trail->down = temp;
460 	}
461 	else{				/* first entry */
462 	    while((temp != NULL) && (temp->value != *buf)){
463 		trail = temp;
464 		temp = temp->left;
465 	    }
466 
467 	    if(temp == NULL){   /* add new val */
468 		temp = newnode();
469 		temp->value = *buf;
470 		temp->func = 0;
471 		temp->left = NULL;
472 		temp->down = NULL;
473 		trail->left = temp;
474 	    }
475 	}
476 
477 	if(*(++buf) == '\0')
478 	  break;
479 	else{
480 	    /*
481 	     * Ignore attempt to overwrite shorter existing escape sequence.
482 	     * That means that sequences with higher priority should be
483 	     * set up first, so if we want termcap sequences to override
484 	     * hardwired sequences, put the kpinsert calls for the
485 	     * termcap sequences first.  (That's what you get if you define
486 	     * TERMCAP_WINS.)
487 	     */
488 	    if(temp->func != 0)
489 	      return;
490 
491 	    trail = temp;
492 	    temp = temp->down;
493 	}
494     }
495 
496     /*
497      * Ignore attempt to overwrite longer sequences we are a prefix
498      * of (down != NULL) and exact same sequence (func != 0).
499      */
500     if(temp != NULL && temp->down == NULL && temp->func == 0)
501       temp->func = kval;
502 }
503 
504 
505 
506 /*
507  * kbdestroy() - kills the key pad function key search tree
508  *		 and frees all lines associated with it
509  *
510  *               Should be called with arg kbesc, the top of the tree.
511  */
512 void
kbdestroy(KBESC_T * kb)513 kbdestroy(KBESC_T *kb)
514 {
515     if(kb){
516 	kbdestroy(kb->left);
517 	kbdestroy(kb->down);
518 	free((char *)kb);
519 	kb = NULL;
520     }
521 }
522 
523 #else /* _WINDOWS */
524 
525 /*
526  * Read in a key.
527  * Do the standard keyboard preprocessing. Convert the keys to the internal
528  * character set.  Resolves escape sequences and returns no-op if global
529  * timeout value exceeded.
530  */
531 UCS
GetKey(void)532 GetKey(void)
533 {
534     UCS			ch = 0;
535     long		timein;
536 
537 
538     ch = NODATA;
539     timein = time(0L);
540 
541     /*
542      * Main character processing loop.
543      */
544     while(!mswin_charavail()) {
545 
546 #ifdef MOUSE
547 	/* Check Mouse.  If we get a mouse event, convert to char
548 	 * event and return that. */
549 	if (checkmouse (&ch,0,0,0)) {
550 	    curwp->w_flag |= WFHARD;
551 	    return (ch);
552 	}
553 #endif /* MOUSE */
554 
555 
556 	/* Check Timeout. */
557 	if(time(0L) >= timein+(FUDGE-10))
558 	    return(NODATA);
559     }
560 
561 
562     return (mswin_getc_fast());
563 }
564 
565 #endif /* _WINDOWS */
566