getch.c revision 1.16 1 /* $NetBSD: getch.c,v 1.16 2000/04/11 13:57:09 blymn Exp $ */
2
3 /*
4 * Copyright (c) 1981, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)getch.c 8.2 (Berkeley) 5/4/94";
40 #else
41 __RCSID("$NetBSD: getch.c,v 1.16 2000/04/11 13:57:09 blymn Exp $");
42 #endif
43 #endif /* not lint */
44
45 #include <string.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <stdio.h>
49 #include "curses.h"
50 #include "curses_private.h"
51
52 #define DEFAULT_DELAY 2 /* default delay for timeout() */
53
54 /*
55 * Keyboard input handler. Do this by snarfing
56 * all the info we can out of the termcap entry for TERM and putting it
57 * into a set of keymaps. A keymap is an array the size of all the possible
58 * single characters we can get, the contents of the array is a structure
59 * that contains the type of entry this character is (i.e. part/end of a
60 * multi-char sequence or a plain char) and either a pointer which will point
61 * to another keymap (in the case of a multi-char sequence) OR the data value
62 * that this key should return.
63 *
64 */
65
66 /* private data structures for holding the key definitions */
67 typedef struct keymap keymap_t;
68 typedef struct key_entry key_entry_t;
69
70 struct key_entry {
71 short type; /* type of key this is */
72 union {
73 keymap_t *next; /* next keymap is key is multi-key sequence */
74 wchar_t symbol; /* key symbol if key is a leaf entry */
75 } value;
76 };
77 /* Types of key structures we can have */
78 #define KEYMAP_MULTI 1 /* part of a multi char sequence */
79 #define KEYMAP_LEAF 2 /* key has a symbol associated with it, either
80 * it is the end of a multi-char sequence or a
81 * single char key that generates a symbol */
82
83 /* The max number of different chars we can receive */
84 #define MAX_CHAR 256
85
86 struct keymap {
87 int count; /* count of number of key structs allocated */
88 short mapping[MAX_CHAR]; /* mapping of key to allocated structs */
89 key_entry_t **key; /* dynamic array of keys */};
90
91
92 /* Key buffer */
93 #define INBUF_SZ 16 /* size of key buffer - must be larger than
94 * longest multi-key sequence */
95 static wchar_t inbuf[INBUF_SZ];
96 static int start, end, working; /* pointers for manipulating inbuf data */
97
98 #define INC_POINTER(ptr) do { \
99 (ptr)++; \
100 ptr %= INBUF_SZ; \
101 } while(/*CONSTCOND*/0)
102
103 static short state; /* state of the inkey function */
104
105 #define INKEY_NORM 0 /* no key backlog to process */
106 #define INKEY_ASSEMBLING 1 /* assembling a multi-key sequence */
107 #define INKEY_BACKOUT 2 /* recovering from an unrecognised key */
108 #define INKEY_TIMEOUT 3 /* multi-key sequence timeout */
109
110 /* The termcap data we are interested in and the symbols they map to */
111 struct tcdata {
112 char *name; /* name of termcap entry */
113 wchar_t symbol; /* the symbol associated with it */
114 };
115
116 static const struct tcdata tc[] = {
117 {"K1", KEY_A1},
118 {"K2", KEY_B2},
119 {"K3", KEY_A3},
120 {"K4", KEY_C1},
121 {"K5", KEY_C3},
122 {"k0", KEY_F0},
123 {"k1", KEY_F(1)},
124 {"k2", KEY_F(2)},
125 {"k3", KEY_F(3)},
126 {"k4", KEY_F(4)},
127 {"k5", KEY_F(5)},
128 {"k6", KEY_F(6)},
129 {"k7", KEY_F(7)},
130 {"k8", KEY_F(8)},
131 {"k9", KEY_F(9)},
132 {"kA", KEY_IL},
133 {"ka", KEY_CATAB},
134 {"kb", KEY_BACKSPACE},
135 {"kC", KEY_CLEAR},
136 {"kD", KEY_DC},
137 {"kd", KEY_DOWN},
138 {"kE", KEY_EOL},
139 {"kF", KEY_SF},
140 {"kH", KEY_LL},
141 {"kh", KEY_HOME},
142 {"kI", KEY_IC},
143 {"kL", KEY_DL},
144 {"kl", KEY_LEFT},
145 {"kN", KEY_NPAGE},
146 {"kP", KEY_PPAGE},
147 {"kR", KEY_SR},
148 {"kr", KEY_RIGHT},
149 {"kS", KEY_EOS},
150 {"kT", KEY_STAB},
151 {"kt", KEY_CTAB},
152 {"ku", KEY_UP}
153 };
154 /* Number of TC entries .... */
155 static const int num_tcs = (sizeof(tc) / sizeof(struct tcdata));
156
157 /* The root keymap */
158
159 static keymap_t *base_keymap;
160
161 /* prototypes for private functions */
162 static keymap_t *new_keymap(void); /* create a new keymap */
163 static key_entry_t *new_key(void); /* create a new key entry */
164 static wchar_t inkey(int, int);
165
166 /*
167 * Init_getch - initialise all the pointers & structures needed to make
168 * getch work in keypad mode.
169 *
170 */
171 void
172 __init_getch(sp)
173 char *sp;
174 {
175 static char termcap[1024];
176 char entry[1024], termname[1024], *p;
177 int i, j, length;
178 keymap_t *current;
179 key_entry_t *the_key;
180
181 /* init the inkey state variable */
182 state = INKEY_NORM;
183
184 /* init the base keymap */
185 base_keymap = new_keymap();
186
187 /* key input buffer pointers */
188 start = end = working = 0;
189
190 /* now do the termcap snarfing ... */
191 (void) strncpy(termname, sp, (size_t) 1022);
192 termname[1023] = 0;
193
194 if (tgetent(termcap, termname) <= 0)
195 return;
196
197 for (i = 0; i < num_tcs; i++) {
198
199 p = entry;
200 if (tgetstr(tc[i].name, &p) == NULL)
201 continue;
202
203 current = base_keymap; /* always start with base keymap. */
204 length = (int) strlen(entry);
205
206 for (j = 0; j < length - 1; j++) {
207 if (current->mapping[(unsigned) entry[j]] < 0) {
208 /* first time for this char */
209 current->mapping[(unsigned) entry[j]] = current->count; /* map new entry */
210 the_key = new_key();
211 /* multikey coz we are here */
212 the_key->type = KEYMAP_MULTI;
213
214 /* need for next key */
215 the_key->value.next = new_keymap();
216
217 /* put into key array */
218 if ((current->key = realloc(current->key, (current->count + 1) * sizeof(key_entry_t *))) == NULL) {
219 fprintf(stderr,
220 "Could not malloc for key entry\n");
221 exit(1);
222 }
223
224 current->key[current->count++] = the_key;
225
226 }
227 /* next key uses this map... */
228 current = current->key[current->mapping[(unsigned) entry[j]]]->value.next;
229 }
230
231 /*
232 * This is the last key in the sequence (it may have been
233 * the only one but that does not matter) this means it is
234 * a leaf key and should have a symbol associated with it.
235 */
236 if (current->count > 0) {
237 /*
238 * If there were other keys then we need to
239 * extend the mapping array.
240 */
241 if ((current->key =
242 realloc(current->key,
243 (current->count + 1) *
244 sizeof(key_entry_t *))) == NULL) {
245
246 fprintf(stderr,
247 "Could not malloc for key entry\n");
248 exit(1);
249 }
250 }
251 current->mapping[(unsigned) entry[length - 1]] = current->count;
252 the_key = new_key();
253 the_key->type = KEYMAP_LEAF; /* leaf key */
254
255 /* the associated symbol */
256 the_key->value.symbol = tc[i].symbol;
257 current->key[current->count++] = the_key;
258 }
259 }
260
261
262 /*
263 * new_keymap - allocates & initialises a new keymap structure. This
264 * function returns a pointer to the new keymap.
265 *
266 */
267 static keymap_t *
268 new_keymap(void)
269 {
270 int i;
271 keymap_t *new_map;
272
273 if ((new_map = malloc(sizeof(keymap_t))) == NULL) {
274 perror("Inkey: Cannot allocate new keymap");
275 exit(2);
276 }
277
278 /* Initialise the new map */
279 new_map->count = 0;
280 for (i = 0; i < MAX_CHAR; i++) {
281 new_map->mapping[i] = -1; /* no mapping for char */
282 }
283
284 /* one does assume there will be at least one key mapped.... */
285 if ((new_map->key = malloc(sizeof(key_entry_t *))) == NULL) {
286 perror("Could not malloc first key ent");
287 exit(1);
288 }
289
290 return (new_map);
291 }
292
293 /*
294 * new_key - allocates & initialises a new key entry. This function returns
295 * a pointer to the newly allocated key entry.
296 *
297 */
298 static key_entry_t *
299 new_key(void)
300 {
301 key_entry_t *new_one;
302
303 if ((new_one = malloc(sizeof(key_entry_t))) == NULL) {
304 perror("inkey: Cannot allocate new key entry");
305 exit(2);
306 }
307 new_one->type = 0;
308 new_one->value.next = NULL;
309
310 return (new_one);
311 }
312
313 /*
314 * inkey - do the work to process keyboard input, check for multi-key
315 * sequences and return the appropriate symbol if we get a match.
316 *
317 */
318
319 wchar_t
320 inkey(to, delay)
321 int to, delay;
322 {
323 wchar_t k;
324 ssize_t nchar;
325 char c;
326 keymap_t *current = base_keymap;
327
328 for (;;) { /* loop until we get a complete key sequence */
329 reread:
330 if (state == INKEY_NORM) {
331 if (delay && __timeout(delay) == ERR)
332 return ERR;
333 if ((nchar = read(STDIN_FILENO, &c, sizeof(char))) < 0)
334 return ERR;
335 if (delay && (__notimeout() == ERR))
336 return ERR;
337 if (nchar == 0)
338 return ERR; /* just in case we are nodelay
339 * mode */
340 k = (wchar_t) c;
341 #ifdef DEBUG
342 __CTRACE("inkey (state normal) got '%s'\n", unctrl(k));
343 #endif
344
345 working = start;
346 inbuf[working] = k;
347 INC_POINTER(working);
348 end = working;
349 state = INKEY_ASSEMBLING; /* go to the assembling
350 * state now */
351 } else if (state == INKEY_BACKOUT) {
352 k = inbuf[working];
353 INC_POINTER(working);
354 if (working == end) { /* see if we have run
355 * out of keys in the
356 * backlog */
357
358 /* if we have then switch to
359 assembling */
360 state = INKEY_ASSEMBLING;
361 }
362 } else if (state == INKEY_ASSEMBLING) {
363 /* assembling a key sequence */
364 if (delay) {
365 if (__timeout(to ? DEFAULT_DELAY : delay) == ERR)
366 return ERR;
367 } else {
368 if (to && (__timeout(DEFAULT_DELAY) == ERR))
369 return ERR;
370 }
371 if ((nchar = read(STDIN_FILENO, &c,
372 sizeof(char))) < 0)
373 return ERR;
374 if ((to || delay) && (__notimeout() == ERR))
375 return ERR;
376
377 k = (wchar_t) c;
378 #ifdef DEBUG
379 __CTRACE("inkey (state assembling) got '%s'\n", unctrl(k));
380 #endif
381 if (nchar == 0) { /* inter-char timeout,
382 * start backing out */
383 if (start == end)
384 /* no chars in the buffer, restart */
385 goto reread;
386
387 k = inbuf[start];
388 state = INKEY_TIMEOUT;
389 } else {
390 inbuf[working] = k;
391 INC_POINTER(working);
392 end = working;
393 }
394 } else {
395 fprintf(stderr, "Inkey state screwed - exiting!!!");
396 exit(2);
397 }
398
399 /* Check key has no special meaning and we have not timed out */
400 if ((current->mapping[k] < 0) || (state == INKEY_TIMEOUT)) {
401 /* return the first key we know about */
402 k = inbuf[start];
403
404 INC_POINTER(start);
405 working = start;
406
407 if (start == end) { /* only one char processed */
408 state = INKEY_NORM;
409 } else {/* otherwise we must have more than one char
410 * to backout */
411 state = INKEY_BACKOUT;
412 }
413 return k;
414 } else { /* must be part of a multikey sequence */
415 /* check for completed key sequence */
416 if (current->key[current->mapping[k]]->type == KEYMAP_LEAF) {
417 start = working; /* eat the key sequence
418 * in inbuf */
419
420 /* check if inbuf empty now */
421 if (start == end) {
422 /* if it is go back to normal */
423 state = INKEY_NORM;
424 } else {
425 /* otherwise go to backout state */
426 state = INKEY_BACKOUT;
427 }
428
429 /* return the symbol */
430 return current->key[current->mapping[k]]->value.symbol;
431
432 } else {
433 /*
434 * Step on to next part of the multi-key
435 * sequence.
436 */
437 current = current->key[current->mapping[k]]->value.next;
438 }
439 }
440 }
441 }
442
443 /*
444 * wgetch --
445 * Read in a character from the window.
446 */
447 int
448 wgetch(win)
449 WINDOW *win;
450 {
451 int inp, weset;
452 ssize_t nchar;
453 char c;
454
455 if (!(win->flags & __SCROLLOK) && (win->flags & __FULLWIN)
456 && win->curx == win->maxx - 1 && win->cury == win->maxy - 1
457 && __echoit)
458 return (ERR);
459 #ifdef DEBUG
460 __CTRACE("wgetch: __echoit = %d, __rawmode = %d\n",
461 __echoit, __rawmode);
462 #endif
463 if (__echoit && !__rawmode) {
464 cbreak();
465 weset = 1;
466 } else
467 weset = 0;
468
469 __save_termios();
470
471 if (win->flags & __KEYPAD) {
472 switch (win->delay)
473 {
474 case -1:
475 inp = inkey (win->flags & __NOTIMEOUT ? 0 : 1, 0);
476 break;
477 case 0:
478 if (__nodelay() == ERR) return ERR;
479 inp = inkey(0, 0);
480 break;
481 default:
482 inp = inkey(win->flags & __NOTIMEOUT ? 0 : 1, win->delay);
483 break;
484 }
485 } else {
486 switch (win->delay)
487 {
488 case -1:
489 break;
490 case 0:
491 if (__nodelay() == ERR) {
492 __restore_termios();
493 return ERR;
494 }
495 break;
496 default:
497 if (__timeout(win->delay) == ERR) {
498 __restore_termios();
499 return ERR;
500 }
501 break;
502 }
503
504 if ((nchar = read(STDIN_FILENO, &c, sizeof(char))) < 0) {
505 inp = ERR;
506 } else {
507 if (nchar == 0) {
508 __restore_termios();
509 return ERR; /* we have timed out */
510 }
511 inp = (unsigned int) c;
512 }
513 }
514 #ifdef DEBUG
515 if (inp > 255)
516 /* we have a key symbol - treat it differently */
517 /* XXXX perhaps __unctrl should be expanded to include
518 * XXXX the keysyms in the table....
519 */
520 __CTRACE("wgetch assembled keysym 0x%x\n", inp);
521 else
522 __CTRACE("wgetch got '%s'\n", unctrl(inp));
523 #endif
524 if (win->delay > -1) {
525 if (__delay() == ERR) {
526 __restore_termios();
527 return ERR;
528 }
529 }
530
531 __restore_termios();
532 if (__echoit) {
533 mvwaddch(curscr,
534 (int) (win->cury + win->begy), (int) (win->curx + win->begx), (chtype) inp);
535 waddch(win, (chtype) inp);
536 }
537 if (weset)
538 nocbreak();
539
540 return ((inp < 0) || (inp == ERR) ? ERR : inp);
541 }
542