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