* Update to version 2.19.5
[alpine.git] / pico / osdep / getkey.c
blob68ad707c3f18fe7b2451969c1f3504d2dfa8230e
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
5 /*
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2014 Eduardo Chappa
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
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include <system.h>
20 #include <general.h>
22 #include "../estruct.h"
23 #include "../mode.h"
24 #include "../pico.h"
25 #include "../edef.h"
26 #include "../efunc.h"
27 #include "../keydefs.h"
29 #include "tty.h"
30 #include "getkey.h"
31 #include "read.h"
32 #include "mouse.h"
34 #ifdef _WINDOWS
35 #include "mswin.h"
36 static int MapMSKEYtoPK(int c);
37 #endif /* _WINDOWS */
40 /* internal declarations */
41 static int timeo = 0;
43 /* these next two are declared in pith/conf.h */
44 int
45 set_input_timeout(int t)
47 int oldtimeo = timeo;
49 timeo = t;
50 return(oldtimeo);
53 int
54 get_input_timeout(void)
56 return(timeo);
60 #ifndef _WINDOWS
63 /* internal prototypes */
64 void bail(void);
65 int ReadyForKey(int);
69 void
70 bail(void)
72 sleep(30); /* see if os receives SIGHUP */
73 kill(getpid(), SIGHUP); /* eof or bad error */
77 #if TYPEAH
78 /*
79 * typahead - Check to see if any characters are already in the
80 * keyboard buffer
82 int
83 typahead(void)
85 int x; /* holds # of pending chars */
87 return((ioctl(0,FIONREAD,&x) < 0) ? 0 : x);
89 #endif /* TYPEAH */
94 * ReadyForKey - return true if there's no timeout or we're told input
95 * is available...
97 int
98 ReadyForKey(int timeout)
100 switch(input_ready(timeout)){
101 case READY_TO_READ:
102 return(1);
103 break;
105 case NO_OP_COMMAND:
106 case NO_OP_IDLE:
107 case READ_INTR:
108 return(0);
110 case BAIL_OUT:
111 case PANIC_NOW:
112 emlwrite("\007Problem reading from keyboard!", NULL);
113 kill(getpid(), SIGHUP); /* Bomb out (saving our work)! */
114 /* no return */
117 /* can't happen */
118 return(0);
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.
130 GetKey(void)
132 UCS ch, status, cc;
134 if(!ReadyForKey(FUDGE-5))
135 return(NODATA);
137 switch(status = kbseq(simple_ttgetc, NULL, bail, input_cs, &ch)){
138 case 0: /* regular character */
139 break;
141 case KEY_DOUBLE_ESC:
143 * Special hack to get around comm devices eating control characters.
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;
173 case 0: /* regular character */
174 break;
176 default: /* punt the whole thing */
177 (*term.t_beep)();
178 return(BADESC);
179 break;
182 ch &= 0x7f;
183 if(isdigit((unsigned char)ch)){
184 int n = 0, i = ch - '0';
186 if(i < 0 || i > 2)
187 return(BADESC); /* bogus literal char value */
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);
198 i = (i * 10) + (ch - '0');
201 ch = i;
203 else{
204 if(islower((unsigned char)ch)) /* canonicalize if alpha */
205 ch = toupper((unsigned char)ch);
207 return((isalpha((unsigned char)ch) || ch == '@'
208 || (ch >= '[' && ch <= '_'))
209 ? (CTRL | ch) : ((ch == ' ') ? (CTRL | '@') : ch));
212 break;
214 #ifdef MOUSE
215 case KEY_XTERM_MOUSE:
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...
223 static int down = 0;
224 int x, y, button;
225 unsigned long cmd;
227 button = (*term.t_getchar)(NODATA, NULL, bail) & 0x03;
229 x = (*term.t_getchar)(NODATA, NULL, bail) - '!';
230 y = (*term.t_getchar)(NODATA, NULL, bail) - '!';
232 if(button == 0){
233 down = 1;
234 if(checkmouse(&cmd, 1, x, y))
235 return((UCS) cmd);
237 else if(down && button == 3){
238 down = 0;
239 if(checkmouse(&cmd, 0, x, y))
240 return((UCS) cmd);
243 return(NODATA);
246 break;
247 #endif /* MOUSE */
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);
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);
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:
288 if(!ReadyForKey(2)){
289 status = BADESC;
290 break;
292 while(!strchr("~qz", (*term.t_getchar)(NODATA, NULL, bail)));
294 return((status == BADESC)
295 ? status
296 : status - (KEY_SWAL_UP - KEY_UP));
297 break;
299 case KEY_KERMIT:
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 != '\\');
308 ch = NODATA;
309 break;
311 case BADESC:
312 (*term.t_beep)();
313 return(status);
315 default: /* punt the whole thing */
316 (*term.t_beep)();
317 break;
320 if (ch >= 0x00 && ch <= 0x1F) /* C0 control -> C- */
321 ch = CTRL | (ch+'@');
323 return(ch);
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.
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.
339 * Returns: BADESC
340 * The escaped function.
341 * 0 if a regular char with char stuffed in location c.
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)
350 unsigned char c;
351 int first = 1;
352 KBESC_T *current;
354 current = kbesc;
355 if(current == NULL) /* bag it */
356 return(BADESC);
358 while(1){
359 c = (*getcfunc)(recorder, bail_handler);
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];
370 * Regular character.
371 * Read enough bytes to make up a character and convert it to UCS-4.
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:
383 * Not really a BADESC but that ought to
384 * be sufficient. We can add another type if
385 * we need to.
387 return(BADESC);
389 case CCONV_NEEDMORE:
390 if(octets_so_far >= sizeof(inputbuf))
391 return(BADESC);
393 c = (*getcfunc)(recorder, bail_handler);
394 inputbuf[octets_so_far++] = c;
395 break;
397 default:
398 /* got a good UCS-4 character */
399 *ch = ucs;
400 return(0);
404 /* NOTREACHED */
405 return(0);
407 else
408 return(BADESC);
410 current = current->left;
413 if(current->down == NULL) /* match!!!*/
414 return(current->func);
415 else
416 current = current->down;
418 first = 0;
423 #define newnode() (KBESC_T *)malloc(sizeof(KBESC_T))
426 * kpinsert - insert a keystroke escape sequence into the global search
427 * structure.
429 void
430 kpinsert(char *kstr, int kval, int termcap_wins)
432 register char *buf;
433 register KBESC_T *temp;
434 register KBESC_T *trail;
436 if(kstr == NULL)
437 return;
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.
443 if(!termcap_wins && *kstr != '\033')
444 return;
446 temp = trail = kbesc;
447 buf = kstr;
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;
461 else{ /* first entry */
462 while((temp != NULL) && (temp->value != *buf)){
463 trail = temp;
464 temp = temp->left;
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;
477 if(*(++buf) == '\0')
478 break;
479 else{
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.)
488 if(temp->func != 0)
489 return;
491 trail = temp;
492 temp = temp->down;
497 * Ignore attempt to overwrite longer sequences we are a prefix
498 * of (down != NULL) and exact same sequence (func != 0).
500 if(temp != NULL && temp->down == NULL && temp->func == 0)
501 temp->func = kval;
507 * kbdestroy() - kills the key pad function key search tree
508 * and frees all lines associated with it
510 * Should be called with arg kbesc, the top of the tree.
512 void
513 kbdestroy(KBESC_T *kb)
515 if(kb){
516 kbdestroy(kb->left);
517 kbdestroy(kb->down);
518 free((char *)kb);
519 kb = NULL;
523 #else /* _WINDOWS */
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.
532 GetKey(void)
534 UCS ch = 0;
535 long timein;
538 ch = NODATA;
539 timein = time(0L);
542 * Main character processing loop.
544 while(!mswin_charavail()) {
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);
553 #endif /* MOUSE */
556 /* Check Timeout. */
557 if(time(0L) >= timein+(FUDGE-10))
558 return(NODATA);
562 return (mswin_getc_fast());
565 #endif /* _WINDOWS */