Add support for tab-completion when selecting by rule
[alpine.git] / pico / osdep / getkey.c
blob5ebca599d738cbf562ebdf4ce29135eaded06fba
1 /*
2 * ========================================================================
3 * Copyright 2006-2007 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include <system.h>
16 #include <general.h>
18 #include "../estruct.h"
19 #include "../mode.h"
20 #include "../pico.h"
21 #include "../edef.h"
22 #include "../efunc.h"
23 #include "../keydefs.h"
25 #include "tty.h"
26 #include "getkey.h"
27 #include "read.h"
28 #include "mouse.h"
30 #ifdef _WINDOWS
31 #include "mswin.h"
32 static int MapMSKEYtoPK(int c);
33 #endif /* _WINDOWS */
36 /* internal declarations */
37 static int timeo = 0;
39 /* these next two are declared in pith/conf.h */
40 int
41 set_input_timeout(int t)
43 int oldtimeo = timeo;
45 timeo = t;
46 return(oldtimeo);
49 int
50 get_input_timeout(void)
52 return(timeo);
56 #ifndef _WINDOWS
59 /* internal prototypes */
60 void bail(void);
61 int ReadyForKey(int);
65 void
66 bail(void)
68 sleep(30); /* see if os receives SIGHUP */
69 kill(getpid(), SIGHUP); /* eof or bad error */
73 #if TYPEAH
74 /*
75 * typahead - Check to see if any characters are already in the
76 * keyboard buffer
78 int
79 typahead(void)
81 int x; /* holds # of pending chars */
83 return((ioctl(0,FIONREAD,&x) < 0) ? 0 : x);
85 #endif /* TYPEAH */
90 * ReadyForKey - return true if there's no timeout or we're told input
91 * is available...
93 int
94 ReadyForKey(int timeout)
96 switch(input_ready(timeout)){
97 case READY_TO_READ:
98 return(1);
99 break;
101 case NO_OP_COMMAND:
102 case NO_OP_IDLE:
103 case READ_INTR:
104 return(0);
106 case BAIL_OUT:
107 case PANIC_NOW:
108 emlwwrite(_("Problem reading from keyboard!"), NULL);
109 kill(getpid(), SIGHUP); /* Bomb out (saving our work)! */
110 /* no return */
113 /* can't happen */
114 return(0);
120 * GetKey - Read in a key.
121 * Do the standard keyboard preprocessing. Convert the keys to the internal
122 * character set. Resolves escape sequences and returns no-op if global
123 * timeout value exceeded.
126 GetKey(void)
128 UCS ch, status, cc;
130 if(!ReadyForKey(FUDGE-5))
131 return(NODATA);
133 switch(status = kbseq(simple_ttgetc, NULL, bail, input_cs, &ch)){
134 case 0: /* regular character */
135 break;
137 case KEY_DOUBLE_ESC:
139 * Special hack to get around comm devices eating control characters.
141 if(!ReadyForKey(5))
142 return(BADESC); /* user typed ESC ESC, then stopped */
143 else
144 switch(status = kbseq(simple_ttgetc, NULL, bail, input_cs, &ch)){
145 case KEY_UP :
146 case KEY_DOWN :
147 case KEY_RIGHT :
148 case KEY_LEFT :
149 case KEY_PGUP :
150 case KEY_PGDN :
151 case KEY_HOME :
152 case KEY_END :
153 case KEY_DEL :
154 case F1 :
155 case F2 :
156 case F3 :
157 case F4 :
158 case F5 :
159 case F6 :
160 case F7 :
161 case F8 :
162 case F9 :
163 case F10 :
164 case F11 :
165 case F12 :
166 return(CTRL | status);
167 break;
169 case 0: /* regular character */
170 break;
172 default: /* punt the whole thing */
173 (*term.t_beep)();
174 return(BADESC);
175 break;
178 ch &= 0x7f;
179 if(isdigit((unsigned char)ch)){
180 int n = 0, i = ch - '0';
182 if(i < 0 || i > 2)
183 return(BADESC); /* bogus literal char value */
185 while(n++ < 2){
186 if(!ReadyForKey(5)
187 || (!isdigit((unsigned char) (ch =
188 (*term.t_getchar)(NODATA, NULL, bail)))
189 || (n == 1 && i == 2 && ch > '5')
190 || (n == 2 && i == 25 && ch > '5'))){
191 return(BADESC);
194 i = (i * 10) + (ch - '0');
197 ch = i;
199 else{
200 if(islower((unsigned char)ch)) /* canonicalize if alpha */
201 ch = toupper((unsigned char)ch);
203 return((isalpha((unsigned char)ch) || ch == '@'
204 || (ch >= '[' && ch <= '_'))
205 ? (CTRL | ch) : ((ch == ' ') ? (CTRL | '@') : ch));
208 break;
210 #ifdef MOUSE
211 case KEY_XTERM_MOUSE:
214 * Special hack to get mouse events from an xterm.
215 * Get the details, then pass it past the keymenu event
216 * handler, and then to the installed handler if there
217 * is one...
219 static int down = 0;
220 int x, y, button;
221 unsigned long cmd;
223 button = (*term.t_getchar)(NODATA, NULL, bail) & 0x03;
225 x = (*term.t_getchar)(NODATA, NULL, bail) - '!';
226 y = (*term.t_getchar)(NODATA, NULL, bail) - '!';
228 if(button == 0){
229 down = 1;
230 if(checkmouse(&cmd, 1, x, y))
231 return((UCS) cmd);
233 else if(down && button == 3){
234 down = 0;
235 if(checkmouse(&cmd, 0, x, y))
236 return((UCS) cmd);
239 return(NODATA);
242 break;
243 #endif /* MOUSE */
245 case KEY_UP :
246 case KEY_DOWN :
247 case KEY_RIGHT :
248 case KEY_LEFT :
249 case KEY_PGUP :
250 case KEY_PGDN :
251 case KEY_HOME :
252 case KEY_END :
253 case KEY_DEL :
254 case F1 :
255 case F2 :
256 case F3 :
257 case F4 :
258 case F5 :
259 case F6 :
260 case F7 :
261 case F8 :
262 case F9 :
263 case F10 :
264 case F11 :
265 case F12 :
266 return(status);
268 case CTRL_KEY_UP :
269 return(CTRL | KEY_UP);
270 case CTRL_KEY_DOWN :
271 return(CTRL | KEY_DOWN);
272 case CTRL_KEY_RIGHT :
273 return(CTRL | KEY_RIGHT);
274 case CTRL_KEY_LEFT :
275 return(CTRL | KEY_LEFT);
277 case KEY_SWALLOW_Z:
278 status = BADESC;
279 case KEY_SWAL_UP:
280 case KEY_SWAL_DOWN:
281 case KEY_SWAL_LEFT:
282 case KEY_SWAL_RIGHT:
284 if(!ReadyForKey(2)){
285 status = BADESC;
286 break;
288 while(!strchr("~qz", (*term.t_getchar)(NODATA, NULL, bail)));
290 return((status == BADESC)
291 ? status
292 : status - (KEY_SWAL_UP - KEY_UP));
293 break;
295 case KEY_KERMIT:
297 cc = ch;
298 if(!ReadyForKey(2))
299 return(BADESC);
300 else
301 ch = (*term.t_getchar)(NODATA, NULL, bail) & 0x7f;
302 }while(cc != '\033' && ch != '\\');
304 ch = NODATA;
305 break;
307 case BADESC:
308 (*term.t_beep)();
309 return(status);
311 default: /* punt the whole thing */
312 (*term.t_beep)();
313 break;
316 if (ch >= 0x00 && ch <= 0x1F) /* C0 control -> C- */
317 ch = CTRL | (ch+'@');
319 return(ch);
324 * kbseq - looks at an escape sequence coming from the keyboard and
325 * compares it to a trie of known keyboard escape sequences, and
326 * returns the function bound to the escape sequence.
328 * Args: getcfunc -- Function to get a single character from stdin,
329 * called with the next two arguments to this
330 * function as its arguments.
331 * recorder -- If non-NULL, function used to record keystroke.
332 * bail_handler -- Function used to bail out on read error.
333 * c -- Pointer to returned character.
335 * Returns: BADESC
336 * The escaped function.
337 * 0 if a regular char with char stuffed in location c.
340 kbseq(int (*getcfunc)(int (*recorder)(int ), void (*bail_handler)(void )),
341 int (*recorder)(int),
342 void (*bail_handler)(void),
343 void *data,
344 UCS *ch)
346 unsigned char c;
347 int first = 1;
348 KBESC_T *current;
350 current = kbesc;
351 if(current == NULL) /* bag it */
352 return(BADESC);
354 while(1){
355 c = (*getcfunc)(recorder, bail_handler);
357 while(current->value != c){
358 if(current->left == NULL){ /* NO MATCH */
359 if(first){
360 unsigned long octets_so_far, remaining_octets;
361 unsigned char *inputp;
362 UCS ucs;
363 unsigned char inputbuf[20];
366 * Regular character.
367 * Read enough bytes to make up a character and convert it to UCS-4.
369 memset(inputbuf, 0, sizeof(inputbuf));
370 inputbuf[0] = c;
371 octets_so_far = 1;
372 for(;;){
373 remaining_octets = octets_so_far;
374 inputp = inputbuf;
375 ucs = mbtow(data, &inputp, &remaining_octets);
376 switch(ucs){
377 case CCONV_BADCHAR:
379 * Not really a BADESC but that ought to
380 * be sufficient. We can add another type if
381 * we need to.
383 return(BADESC);
385 case CCONV_NEEDMORE:
386 if(octets_so_far >= sizeof(inputbuf))
387 return(BADESC);
389 c = (*getcfunc)(recorder, bail_handler);
390 inputbuf[octets_so_far++] = c;
391 break;
393 default:
394 /* got a good UCS-4 character */
395 *ch = ucs;
396 return(0);
400 /* NOTREACHED */
401 return(0);
403 else
404 return(BADESC);
406 current = current->left;
409 if(current->down == NULL) /* match!!!*/
410 return(current->func);
411 else
412 current = current->down;
414 first = 0;
419 #define newnode() (KBESC_T *)malloc(sizeof(KBESC_T))
422 * kpinsert - insert a keystroke escape sequence into the global search
423 * structure.
425 void
426 kpinsert(char *kstr, int kval, int termcap_wins)
428 register char *buf;
429 register KBESC_T *temp;
430 register KBESC_T *trail;
432 if(kstr == NULL)
433 return;
436 * Don't allow escape sequences that don't start with ESC unless
437 * termcap_wins. This is to protect against mistakes in termcap files.
439 if(!termcap_wins && *kstr != '\033')
440 return;
442 temp = trail = kbesc;
443 buf = kstr;
445 for(;;){
446 if(temp == NULL){
447 temp = newnode();
448 temp->value = *buf;
449 temp->func = 0;
450 temp->left = NULL;
451 temp->down = NULL;
452 if(kbesc == NULL)
453 kbesc = temp;
454 else
455 trail->down = temp;
457 else{ /* first entry */
458 while((temp != NULL) && (temp->value != *buf)){
459 trail = temp;
460 temp = temp->left;
463 if(temp == NULL){ /* add new val */
464 temp = newnode();
465 temp->value = *buf;
466 temp->func = 0;
467 temp->left = NULL;
468 temp->down = NULL;
469 trail->left = temp;
473 if(*(++buf) == '\0')
474 break;
475 else{
477 * Ignore attempt to overwrite shorter existing escape sequence.
478 * That means that sequences with higher priority should be
479 * set up first, so if we want termcap sequences to override
480 * hardwired sequences, put the kpinsert calls for the
481 * termcap sequences first. (That's what you get if you define
482 * TERMCAP_WINS.)
484 if(temp->func != 0)
485 return;
487 trail = temp;
488 temp = temp->down;
493 * Ignore attempt to overwrite longer sequences we are a prefix
494 * of (down != NULL) and exact same sequence (func != 0).
496 if(temp != NULL && temp->down == NULL && temp->func == 0)
497 temp->func = kval;
503 * kbdestroy() - kills the key pad function key search tree
504 * and frees all lines associated with it
506 * Should be called with arg kbesc, the top of the tree.
508 void
509 kbdestroy(KBESC_T *kb)
511 if(kb){
512 kbdestroy(kb->left);
513 kbdestroy(kb->down);
514 free((char *)kb);
515 kb = NULL;
519 #else /* _WINDOWS */
522 * Read in a key.
523 * Do the standard keyboard preprocessing. Convert the keys to the internal
524 * character set. Resolves escape sequences and returns no-op if global
525 * timeout value exceeded.
528 GetKey(void)
530 UCS ch = 0;
531 long timein;
534 ch = NODATA;
535 timein = time(0L);
538 * Main character processing loop.
540 while(!mswin_charavail()) {
542 #ifdef MOUSE
543 /* Check Mouse. If we get a mouse event, convert to char
544 * event and return that. */
545 if (checkmouse (&ch,0,0,0)) {
546 curwp->w_flag |= WFHARD;
547 return (ch);
549 #endif /* MOUSE */
552 /* Check Timeout. */
553 if(time(0L) >= timein+(FUDGE-10))
554 return(NODATA);
558 return (mswin_getc_fast());
561 #endif /* _WINDOWS */