getch.c revision 1.19 1 /* $NetBSD: getch.c,v 1.19 2000/04/15 22:59:05 jdc 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.19 2000/04/15 22:59:05 jdc 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(char *sp)
173 {
174 static struct tinfo *termcap;
175 char entry[1024], termname[1024], *p;
176 int i, j, length;
177 size_t limit;
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 (t_getent(&termcap, termname) <= 0)
195 return;
196
197 for (i = 0; i < num_tcs; i++) {
198
199 p = entry;
200 limit = 1023;
201 if (t_getstr(termcap, tc[i].name, &p, &limit) == NULL)
202 continue;
203
204 current = base_keymap; /* always start with base keymap. */
205 length = (int) strlen(entry);
206
207 for (j = 0; j < length - 1; j++) {
208 if (current->mapping[(unsigned) entry[j]] < 0) {
209 /* first time for this char */
210 current->mapping[(unsigned) entry[j]] = current->count; /* map new entry */
211 the_key = new_key();
212 /* multikey coz we are here */
213 the_key->type = KEYMAP_MULTI;
214
215 /* need for next key */
216 the_key->value.next = new_keymap();
217
218 /* put into key array */
219 if ((current->key = realloc(current->key, (current->count + 1) * sizeof(key_entry_t *))) == NULL) {
220 fprintf(stderr,
221 "Could not malloc for key entry\n");
222 exit(1);
223 }
224
225 current->key[current->count++] = the_key;
226
227 }
228 /* next key uses this map... */
229 current = current->key[current->mapping[(unsigned) entry[j]]]->value.next;
230 }
231
232 /*
233 * This is the last key in the sequence (it may have been
234 * the only one but that does not matter) this means it is
235 * a leaf key and should have a symbol associated with it.
236 */
237 if (current->count > 0) {
238 /*
239 * If there were other keys then we need to
240 * extend the mapping array.
241 */
242 if ((current->key =
243 realloc(current->key,
244 (current->count + 1) *
245 sizeof(key_entry_t *))) == NULL) {
246
247 fprintf(stderr,
248 "Could not malloc for key entry\n");
249 exit(1);
250 }
251 }
252 current->mapping[(unsigned) entry[length - 1]] = current->count;
253 the_key = new_key();
254 the_key->type = KEYMAP_LEAF; /* leaf key */
255
256 /* the associated symbol */
257 the_key->value.symbol = tc[i].symbol;
258 current->key[current->count++] = the_key;
259 }
260 }
261
262
263 /*
264 * new_keymap - allocates & initialises a new keymap structure. This
265 * function returns a pointer to the new keymap.
266 *
267 */
268 static keymap_t *
269 new_keymap(void)
270 {
271 int i;
272 keymap_t *new_map;
273
274 if ((new_map = malloc(sizeof(keymap_t))) == NULL) {
275 perror("Inkey: Cannot allocate new keymap");
276 exit(2);
277 }
278
279 /* Initialise the new map */
280 new_map->count = 0;
281 for (i = 0; i < MAX_CHAR; i++) {
282 new_map->mapping[i] = -1; /* no mapping for char */
283 }
284
285 /* one does assume there will be at least one key mapped.... */
286 if ((new_map->key = malloc(sizeof(key_entry_t *))) == NULL) {
287 perror("Could not malloc first key ent");
288 exit(1);
289 }
290
291 return (new_map);
292 }
293
294 /*
295 * new_key - allocates & initialises a new key entry. This function returns
296 * a pointer to the newly allocated key entry.
297 *
298 */
299 static key_entry_t *
300 new_key(void)
301 {
302 key_entry_t *new_one;
303
304 if ((new_one = malloc(sizeof(key_entry_t))) == NULL) {
305 perror("inkey: Cannot allocate new key entry");
306 exit(2);
307 }
308 new_one->type = 0;
309 new_one->value.next = NULL;
310
311 return (new_one);
312 }
313
314 /*
315 * inkey - do the work to process keyboard input, check for multi-key
316 * sequences and return the appropriate symbol if we get a match.
317 *
318 */
319
320 wchar_t
321 inkey(to, delay)
322 int to, delay;
323 {
324 wchar_t k;
325 ssize_t nchar;
326 unsigned char c;
327 keymap_t *current = base_keymap;
328
329 for (;;) { /* loop until we get a complete key sequence */
330 reread:
331 if (state == INKEY_NORM) {
332 if (delay && __timeout(delay) == ERR)
333 return ERR;
334 if ((nchar = read(STDIN_FILENO, &c, sizeof(char))) < 0)
335 return ERR;
336 if (delay && (__notimeout() == ERR))
337 return ERR;
338 if (nchar == 0)
339 return ERR; /* just in case we are nodelay
340 * mode */
341 k = (wchar_t) c;
342 #ifdef DEBUG
343 __CTRACE("inkey (state normal) got '%s'\n", unctrl(k));
344 #endif
345
346 working = start;
347 inbuf[working] = k;
348 INC_POINTER(working);
349 end = working;
350 state = INKEY_ASSEMBLING; /* go to the assembling
351 * state now */
352 } else if (state == INKEY_BACKOUT) {
353 k = inbuf[working];
354 INC_POINTER(working);
355 if (working == end) { /* see if we have run
356 * out of keys in the
357 * backlog */
358
359 /* if we have then switch to
360 assembling */
361 state = INKEY_ASSEMBLING;
362 }
363 } else if (state == INKEY_ASSEMBLING) {
364 /* assembling a key sequence */
365 if (delay) {
366 if (__timeout(to ? DEFAULT_DELAY : delay) == ERR)
367 return ERR;
368 } else {
369 if (to && (__timeout(DEFAULT_DELAY) == ERR))
370 return ERR;
371 }
372 if ((nchar = read(STDIN_FILENO, &c,
373 sizeof(char))) < 0)
374 return ERR;
375 if ((to || delay) && (__notimeout() == ERR))
376 return ERR;
377
378 k = (wchar_t) c;
379 #ifdef DEBUG
380 __CTRACE("inkey (state assembling) got '%s'\n", unctrl(k));
381 #endif
382 if (nchar == 0) { /* inter-char timeout,
383 * start backing out */
384 if (start == end)
385 /* no chars in the buffer, restart */
386 goto reread;
387
388 k = inbuf[start];
389 state = INKEY_TIMEOUT;
390 } else {
391 inbuf[working] = k;
392 INC_POINTER(working);
393 end = working;
394 }
395 } else {
396 fprintf(stderr, "Inkey state screwed - exiting!!!");
397 exit(2);
398 }
399
400 /* Check key has no special meaning and we have not timed out */
401 if ((current->mapping[k] < 0) || (state == INKEY_TIMEOUT)) {
402 /* return the first key we know about */
403 k = inbuf[start];
404
405 INC_POINTER(start);
406 working = start;
407
408 if (start == end) { /* only one char processed */
409 state = INKEY_NORM;
410 } else {/* otherwise we must have more than one char
411 * to backout */
412 state = INKEY_BACKOUT;
413 }
414 return k;
415 } else { /* must be part of a multikey sequence */
416 /* check for completed key sequence */
417 if (current->key[current->mapping[k]]->type == KEYMAP_LEAF) {
418 start = working; /* eat the key sequence
419 * in inbuf */
420
421 /* check if inbuf empty now */
422 if (start == end) {
423 /* if it is go back to normal */
424 state = INKEY_NORM;
425 } else {
426 /* otherwise go to backout state */
427 state = INKEY_BACKOUT;
428 }
429
430 /* return the symbol */
431 return current->key[current->mapping[k]]->value.symbol;
432
433 } else {
434 /*
435 * Step on to next part of the multi-key
436 * sequence.
437 */
438 current = current->key[current->mapping[k]]->value.next;
439 }
440 }
441 }
442 }
443
444 #ifndef _CURSES_USE_MACROS
445 /*
446 * getch --
447 * Read in a character from stdscr.
448 */
449 int
450 getch(void)
451 {
452 return wgetch(stdscr);
453 }
454
455 /*
456 * mvgetch --
457 * Read in a character from stdscr at the given location.
458 */
459 int
460 mvgetch(int y, int x)
461 {
462 return mvwgetch(stdscr, y, x);
463 }
464
465 /*
466 * mvwgetch --
467 * Read in a character from stdscr at the given location in the
468 * given window.
469 */
470 int
471 mvwgetch(WINDOW *win, int y, int x)
472 {
473 if (wmove(win, y, x) == ERR)
474 return ERR;
475
476 return wgetch(win);
477 }
478
479 #endif
480
481 /*
482 * wgetch --
483 * Read in a character from the window.
484 */
485 int
486 wgetch(WINDOW *win)
487 {
488 int inp, weset;
489 ssize_t nchar;
490 char c;
491
492 if (!(win->flags & __SCROLLOK) && (win->flags & __FULLWIN)
493 && win->curx == win->maxx - 1 && win->cury == win->maxy - 1
494 && __echoit)
495 return (ERR);
496 #ifdef DEBUG
497 __CTRACE("wgetch: __echoit = %d, __rawmode = %d, flags = %0.2o\n",
498 __echoit, __rawmode, win->flags);
499 #endif
500 if (__echoit && !__rawmode) {
501 cbreak();
502 weset = 1;
503 } else
504 weset = 0;
505
506 __save_termios();
507
508 if (win->flags & __KEYPAD) {
509 switch (win->delay)
510 {
511 case -1:
512 inp = inkey (win->flags & __NOTIMEOUT ? 0 : 1, 0);
513 break;
514 case 0:
515 if (__nodelay() == ERR) {
516 __restore_termios();
517 return ERR;
518 }
519 inp = inkey(0, 0);
520 break;
521 default:
522 inp = inkey(win->flags & __NOTIMEOUT ? 0 : 1, win->delay);
523 break;
524 }
525 } else {
526 switch (win->delay)
527 {
528 case -1:
529 break;
530 case 0:
531 if (__nodelay() == ERR) {
532 __restore_termios();
533 return ERR;
534 }
535 break;
536 default:
537 if (__timeout(win->delay) == ERR) {
538 __restore_termios();
539 return ERR;
540 }
541 break;
542 }
543
544 if ((nchar = read(STDIN_FILENO, &c, sizeof(char))) < 0) {
545 inp = ERR;
546 } else {
547 if (nchar == 0) {
548 __restore_termios();
549 return ERR; /* we have timed out */
550 }
551 inp = (unsigned int) c;
552 }
553 }
554 #ifdef DEBUG
555 if (inp > 255)
556 /* we have a key symbol - treat it differently */
557 /* XXXX perhaps __unctrl should be expanded to include
558 * XXXX the keysyms in the table....
559 */
560 __CTRACE("wgetch assembled keysym 0x%x\n", inp);
561 else
562 __CTRACE("wgetch got '%s'\n", unctrl(inp));
563 #endif
564 if (win->delay > -1) {
565 if (__delay() == ERR) {
566 __restore_termios();
567 return ERR;
568 }
569 }
570
571 __restore_termios();
572 if (__echoit) {
573 /*
574 mvwaddch(curscr,
575 (int) (win->cury + win->begy), (int) (win->curx + win->begx), (chtype) inp);
576 */
577 waddch(win, (chtype) inp);
578 }
579 if (weset)
580 nocbreak();
581
582 return ((inp < 0) || (inp == ERR) ? ERR : inp);
583 }
584