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