getch.c revision 1.46.6.2 1 /* $NetBSD: getch.c,v 1.46.6.2 2007/01/21 17:43:35 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. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)getch.c 8.2 (Berkeley) 5/4/94";
36 #else
37 __RCSID("$NetBSD: getch.c,v 1.46.6.2 2007/01/21 17:43:35 jdc Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include <string.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <stdio.h>
45 #include "curses.h"
46 #include "curses_private.h"
47 #include "keymap.h"
48
49 int ESCDELAY = 300; /* Delay in ms between keys for esc seq's */
50
51 /* Key buffer */
52 #define INBUF_SZ 16 /* size of key buffer - must be larger than
53 * longest multi-key sequence */
54 static wchar_t inbuf[INBUF_SZ];
55 static int start, end, working; /* pointers for manipulating inbuf data */
56
57 /* prototypes for private functions */
58 static void add_key_sequence(SCREEN *screen, char *sequence, int key_type);
59 static key_entry_t *add_new_key(keymap_t *current, char ch, int key_type,
60 int symbol);
61 static void delete_key_sequence(keymap_t *current, int key_type);
62 static void do_keyok(keymap_t *current, int key_type, bool flag, int *retval);
63 static keymap_t *new_keymap(void); /* create a new keymap */
64 static key_entry_t *new_key(void); /* create a new key entry */
65 static wchar_t inkey(int to, int delay);
66
67 /*
68 * Free the storage associated with the given keymap
69 */
70 void
71 _cursesi_free_keymap(keymap_t *map)
72 {
73 int i;
74
75 /* check for, and free, child keymaps */
76 for (i = 0; i < MAX_CHAR; i++) {
77 if (map->mapping[i] >= 0) {
78 if (map->key[map->mapping[i]]->type == KEYMAP_MULTI)
79 _cursesi_free_keymap(
80 map->key[map->mapping[i]]->value.next);
81 }
82 }
83
84 /* now free any allocated keymap structs */
85 for (i = 0; i < map->count; i += KEYMAP_ALLOC_CHUNK) {
86 free(map->key[i]);
87 }
88
89 free(map->key);
90 free(map);
91 }
92
93
94 /*
95 * Add a new key entry to the keymap pointed to by current. Entry
96 * contains the character to add to the keymap, type is the type of
97 * entry to add (either multikey or leaf) and symbol is the symbolic
98 * value for a leaf type entry. The function returns a pointer to the
99 * new keymap entry.
100 */
101 static key_entry_t *
102 add_new_key(keymap_t *current, char chr, int key_type, int symbol)
103 {
104 key_entry_t *the_key;
105 int i, ki;
106
107 #ifdef DEBUG
108 __CTRACE(__CTRACE_MISC,
109 "Adding character %s of type %d, symbol 0x%x\n",
110 unctrl(chr), key_type, symbol);
111 #endif
112 if (current->mapping[(unsigned char) chr] < 0) {
113 if (current->mapping[(unsigned char) chr] == MAPPING_UNUSED) {
114 /* first time for this char */
115 current->mapping[(unsigned char) chr] =
116 current->count; /* map new entry */
117 ki = current->count;
118
119 /* make sure we have room in the key array first */
120 if ((current->count & (KEYMAP_ALLOC_CHUNK - 1)) == 0)
121 {
122 if ((current->key =
123 realloc(current->key,
124 ki * sizeof(key_entry_t *)
125 + KEYMAP_ALLOC_CHUNK * sizeof(key_entry_t *))) == NULL) {
126 fprintf(stderr,
127 "Could not malloc for key entry\n");
128 exit(1);
129 }
130
131 the_key = new_key();
132 for (i = 0; i < KEYMAP_ALLOC_CHUNK; i++) {
133 current->key[ki + i] = &the_key[i];
134 }
135 }
136 } else {
137 /* the mapping was used but freed, reuse it */
138 ki = - current->mapping[(unsigned char) chr];
139 current->mapping[(unsigned char) chr] = ki;
140 }
141
142 current->count++;
143
144 /* point at the current key array element to use */
145 the_key = current->key[ki];
146
147 the_key->type = key_type;
148
149 switch (key_type) {
150 case KEYMAP_MULTI:
151 /* need for next key */
152 #ifdef DEBUG
153 __CTRACE(__CTRACE_MISC, "Creating new keymap\n");
154 #endif
155 the_key->value.next = new_keymap();
156 the_key->enable = TRUE;
157 break;
158
159 case KEYMAP_LEAF:
160 /* the associated symbol for the key */
161 #ifdef DEBUG
162 __CTRACE(__CTRACE_MISC, "Adding leaf key\n");
163 #endif
164 the_key->value.symbol = symbol;
165 the_key->enable = TRUE;
166 break;
167
168 default:
169 fprintf(stderr, "add_new_key: bad type passed\n");
170 exit(1);
171 }
172 } else {
173 /* the key is already known - just return the address. */
174 #ifdef DEBUG
175 __CTRACE(__CTRACE_MISC, "Keymap already known\n");
176 #endif
177 the_key = current->key[current->mapping[(unsigned char) chr]];
178 }
179
180 return the_key;
181 }
182
183 /*
184 * Delete the given key symbol from the key mappings for the screen.
185 *
186 */
187 void
188 delete_key_sequence(keymap_t *current, int key_type)
189 {
190 key_entry_t *key;
191 int i;
192
193 /*
194 * we need to iterate over all the keys as there may be
195 * multiple instances of the leaf symbol.
196 */
197 for (i = 0; i < MAX_CHAR; i++) {
198 if (current->mapping[i] < 0)
199 continue; /* no mapping for the key, next! */
200
201 key = current->key[current->mapping[i]];
202
203 if (key->type == KEYMAP_MULTI) {
204 /* have not found the leaf, recurse down */
205 delete_key_sequence(key->value.next, key_type);
206 /* if we deleted the last key in the map, free */
207 if (key->value.next->count == 0)
208 _cursesi_free_keymap(key->value.next);
209 } else if ((key->type == KEYMAP_LEAF)
210 && (key->value.symbol == key_type)) {
211 /*
212 * delete the mapping by negating the current
213 * index - this "holds" the position in the
214 * allocation just in case we later re-add
215 * the key for that mapping.
216 */
217 current->mapping[i] = - current->mapping[i];
218 current->count--;
219 }
220 }
221 }
222
223 /*
224 * Add the sequence of characters given in sequence as the key mapping
225 * for the given key symbol.
226 */
227 void
228 add_key_sequence(SCREEN *screen, char *sequence, int key_type)
229 {
230 key_entry_t *tmp_key;
231 keymap_t *current;
232 int length, j, key_ent;
233
234 #ifdef DEBUG
235 __CTRACE(__CTRACE_MISC, "add_key_sequence: add key sequence: %s(%s)\n",
236 sequence, keyname(key_type));
237 #endif /* DEBUG */
238 current = screen->base_keymap; /* always start with
239 * base keymap. */
240 length = (int) strlen(sequence);
241
242 for (j = 0; j < length - 1; j++) {
243 /* add the entry to the struct */
244 tmp_key = add_new_key(current, sequence[j], KEYMAP_MULTI, 0);
245
246 /* index into the key array - it's
247 clearer if we stash this */
248 key_ent = current->mapping[(unsigned char) sequence[j]];
249
250 current->key[key_ent] = tmp_key;
251
252 /* next key uses this map... */
253 current = current->key[key_ent]->value.next;
254 }
255
256 /*
257 * This is the last key in the sequence (it may have been the
258 * only one but that does not matter) this means it is a leaf
259 * key and should have a symbol associated with it.
260 */
261 tmp_key = add_new_key(current, sequence[length - 1], KEYMAP_LEAF,
262 key_type);
263 current->key[current->mapping[(int)sequence[length - 1]]] = tmp_key;
264 }
265
266 /*
267 * Init_getch - initialise all the pointers & structures needed to make
268 * getch work in keypad mode.
269 *
270 */
271 void
272 __init_getch(SCREEN *screen)
273 {
274 char entry[1024], *p;
275 int i;
276 size_t limit;
277 #ifdef DEBUG
278 int k, length;
279 #endif
280
281 /* init the inkey state variable */
282 state = INKEY_NORM;
283
284 /* init the base keymap */
285 screen->base_keymap = new_keymap();
286
287 /* key input buffer pointers */
288 start = end = working = 0;
289
290 /* now do the termcap snarfing ... */
291
292 for (i = 0; i < num_tcs; i++) {
293 p = entry;
294 limit = 1023;
295 if (t_getstr(screen->cursesi_genbuf, tc[i].name,
296 &p, &limit) != (char *) NULL) {
297 #ifdef DEBUG
298 __CTRACE(__CTRACE_INIT,
299 "Processing termcap entry %s, sequence ",
300 tc[i].name);
301 length = (int) strlen(entry);
302 for (k = 0; k <= length -1; k++)
303 __CTRACE(__CTRACE_INIT, "%s", unctrl(entry[k]));
304 __CTRACE(__CTRACE_INIT, "\n");
305 #endif
306 add_key_sequence(screen, entry, tc[i].symbol);
307 }
308
309 }
310 }
311
312
313 /*
314 * new_keymap - allocates & initialises a new keymap structure. This
315 * function returns a pointer to the new keymap.
316 *
317 */
318 static keymap_t *
319 new_keymap(void)
320 {
321 int i;
322 keymap_t *new_map;
323
324 if ((new_map = malloc(sizeof(keymap_t))) == NULL) {
325 perror("Inkey: Cannot allocate new keymap");
326 exit(2);
327 }
328
329 /* Initialise the new map */
330 new_map->count = 0;
331 for (i = 0; i < MAX_CHAR; i++) {
332 new_map->mapping[i] = MAPPING_UNUSED; /* no mapping for char */
333 }
334
335 /* key array will be allocated when first key is added */
336 new_map->key = NULL;
337
338 return new_map;
339 }
340
341 /*
342 * new_key - allocates & initialises a new key entry. This function returns
343 * a pointer to the newly allocated key entry.
344 *
345 */
346 static key_entry_t *
347 new_key(void)
348 {
349 key_entry_t *new_one;
350 int i;
351
352 if ((new_one = malloc(KEYMAP_ALLOC_CHUNK * sizeof(key_entry_t)))
353 == NULL) {
354 perror("inkey: Cannot allocate new key entry chunk");
355 exit(2);
356 }
357
358 for (i = 0; i < KEYMAP_ALLOC_CHUNK; i++) {
359 new_one[i].type = 0;
360 new_one[i].value.next = NULL;
361 }
362
363 return new_one;
364 }
365
366 /*
367 * inkey - do the work to process keyboard input, check for multi-key
368 * sequences and return the appropriate symbol if we get a match.
369 *
370 */
371
372 wchar_t
373 inkey(int to, int delay)
374 {
375 wchar_t k;
376 int c, mapping;
377 keymap_t *current = _cursesi_screen->base_keymap;
378 FILE *infd = _cursesi_screen->infd;
379
380 k = 0; /* XXX gcc -Wuninitialized */
381
382 #ifdef DEBUG
383 __CTRACE(__CTRACE_INPUT, "inkey (%d, %d)\n", to, delay);
384 #endif
385 for (;;) { /* loop until we get a complete key sequence */
386 reread:
387 if (state == INKEY_NORM) {
388 if (delay && __timeout(delay) == ERR)
389 return ERR;
390 c = getchar();
391 if (_cursesi_screen->resized) {
392 if (c != -1)
393 ungetch(c);
394 _cursesi_screen->resized = 0;
395 clearerr(infd);
396 return KEY_RESIZE;
397 }
398 if (c == EOF) {
399 clearerr(infd);
400 return ERR;
401 }
402
403 if (delay && (__notimeout() == ERR))
404 return ERR;
405
406 k = (wchar_t) c;
407 #ifdef DEBUG
408 __CTRACE(__CTRACE_INPUT,
409 "inkey (state normal) got '%s'\n", unctrl(k));
410 #endif
411
412 working = start;
413 inbuf[working] = k;
414 INC_POINTER(working);
415 end = working;
416 state = INKEY_ASSEMBLING; /* go to the assembling
417 * state now */
418 } else if (state == INKEY_BACKOUT) {
419 k = inbuf[working];
420 INC_POINTER(working);
421 if (working == end) { /* see if we have run
422 * out of keys in the
423 * backlog */
424
425 /* if we have then switch to assembling */
426 state = INKEY_ASSEMBLING;
427 }
428 } else if (state == INKEY_ASSEMBLING) {
429 /* assembling a key sequence */
430 if (delay) {
431 if (__timeout(to ? (ESCDELAY / 100) : delay)
432 == ERR)
433 return ERR;
434 } else {
435 if (to && (__timeout(ESCDELAY / 100) == ERR))
436 return ERR;
437 }
438
439 c = getchar();
440 if (_cursesi_screen->resized) {
441 if (c != -1)
442 ungetch(c);
443 _cursesi_screen->resized = 0;
444 clearerr(infd);
445 return KEY_RESIZE;
446 }
447 if (c == -1 || ferror(infd)) {
448 clearerr(infd);
449 return ERR;
450 }
451
452 if ((to || delay) && (__notimeout() == ERR))
453 return ERR;
454
455 k = (wchar_t) c;
456 #ifdef DEBUG
457 __CTRACE(__CTRACE_INPUT,
458 "inkey (state assembling) got '%s'\n", unctrl(k));
459 #endif
460 if (feof(infd)) { /* inter-char timeout,
461 * start backing out */
462 clearerr(infd);
463 if (start == end)
464 /* no chars in the buffer, restart */
465 goto reread;
466
467 k = inbuf[start];
468 state = INKEY_TIMEOUT;
469 } else {
470 inbuf[working] = k;
471 INC_POINTER(working);
472 end = working;
473 }
474 } else {
475 fprintf(stderr, "Inkey state screwed - exiting!!!");
476 exit(2);
477 }
478
479 /*
480 * Check key has no special meaning and we have not
481 * timed out and the key has not been disabled
482 */
483 mapping = current->mapping[k];
484 if (((state == INKEY_TIMEOUT) || (mapping < 0))
485 || ((current->key[mapping]->type == KEYMAP_LEAF)
486 && (current->key[mapping]->enable == FALSE))) {
487 /* return the first key we know about */
488 k = inbuf[start];
489
490 INC_POINTER(start);
491 working = start;
492
493 if (start == end) { /* only one char processed */
494 state = INKEY_NORM;
495 } else {/* otherwise we must have more than one char
496 * to backout */
497 state = INKEY_BACKOUT;
498 }
499 return k;
500 } else { /* must be part of a multikey sequence */
501 /* check for completed key sequence */
502 if (current->key[current->mapping[k]]->type == KEYMAP_LEAF) {
503 start = working; /* eat the key sequence
504 * in inbuf */
505
506 /* check if inbuf empty now */
507 if (start == end) {
508 /* if it is go back to normal */
509 state = INKEY_NORM;
510 } else {
511 /* otherwise go to backout state */
512 state = INKEY_BACKOUT;
513 }
514
515 /* return the symbol */
516 return current->key[current->mapping[k]]->value.symbol;
517
518 } else {
519 /*
520 * Step on to next part of the multi-key
521 * sequence.
522 */
523 current = current->key[current->mapping[k]]->value.next;
524 }
525 }
526 }
527 }
528
529 #ifndef _CURSES_USE_MACROS
530 /*
531 * getch --
532 * Read in a character from stdscr.
533 */
534 int
535 getch(void)
536 {
537 return wgetch(stdscr);
538 }
539
540 /*
541 * mvgetch --
542 * Read in a character from stdscr at the given location.
543 */
544 int
545 mvgetch(int y, int x)
546 {
547 return mvwgetch(stdscr, y, x);
548 }
549
550 /*
551 * mvwgetch --
552 * Read in a character from stdscr at the given location in the
553 * given window.
554 */
555 int
556 mvwgetch(WINDOW *win, int y, int x)
557 {
558 if (wmove(win, y, x) == ERR)
559 return ERR;
560
561 return wgetch(win);
562 }
563
564 #endif
565
566 /*
567 * keyok --
568 * Set the enable flag for a keysym, if the flag is false then
569 * getch will not return this keysym even if the matching key sequence
570 * is seen.
571 */
572 int
573 keyok(int key_type, bool flag)
574 {
575 int result = ERR;
576
577 do_keyok(_cursesi_screen->base_keymap, key_type, flag, &result);
578 return result;
579 }
580
581 /*
582 * do_keyok --
583 * Does the actual work for keyok, we need to recurse through the
584 * keymaps finding the passed key symbol.
585 */
586 void
587 do_keyok(keymap_t *current, int key_type, bool flag, int *retval)
588 {
589 key_entry_t *key;
590 int i;
591
592 /*
593 * we need to iterate over all the keys as there may be
594 * multiple instances of the leaf symbol.
595 */
596 for (i = 0; i < MAX_CHAR; i++) {
597 if (current->mapping[i] < 0)
598 continue; /* no mapping for the key, next! */
599
600 key = current->key[current->mapping[i]];
601
602 if (key->type == KEYMAP_MULTI)
603 do_keyok(key->value.next, key_type, flag, retval);
604 else if ((key->type == KEYMAP_LEAF)
605 && (key->value.symbol == key_type)) {
606 key->enable = flag;
607 *retval = OK; /* we found at least one instance, ok */
608 }
609 }
610 }
611
612 /*
613 * define_key --
614 * Add a custom mapping of a key sequence to key symbol.
615 *
616 */
617 int
618 define_key(char *sequence, int symbol)
619 {
620
621 if (symbol <= 0)
622 return ERR;
623
624 if (sequence == NULL)
625 delete_key_sequence(_cursesi_screen->base_keymap, symbol);
626 else
627 add_key_sequence(_cursesi_screen, sequence, symbol);
628
629 return OK;
630 }
631
632 /*
633 * wgetch --
634 * Read in a character from the window.
635 */
636 int
637 wgetch(WINDOW *win)
638 {
639 int inp, weset;
640 int c;
641 FILE *infd = _cursesi_screen->infd;
642
643 if (!(win->flags & __SCROLLOK) && (win->flags & __FULLWIN)
644 && win->curx == win->maxx - 1 && win->cury == win->maxy - 1
645 && __echoit)
646 return (ERR);
647
648 if (is_wintouched(win))
649 wrefresh(win);
650 #ifdef DEBUG
651 __CTRACE(__CTRACE_INPUT, "wgetch: __echoit = %d, "
652 "__rawmode = %d, __nl = %d, flags = %#.4x\n",
653 __echoit, __rawmode, _cursesi_screen->nl, win->flags);
654 #endif
655 if (__echoit && !__rawmode) {
656 cbreak();
657 weset = 1;
658 } else
659 weset = 0;
660
661 __save_termios();
662
663 if (win->flags & __KEYPAD) {
664 switch (win->delay)
665 {
666 case -1:
667 inp = inkey (win->flags & __NOTIMEOUT ? 0 : 1, 0);
668 break;
669 case 0:
670 if (__nodelay() == ERR) {
671 __restore_termios();
672 return ERR;
673 }
674 inp = inkey(0, 0);
675 break;
676 default:
677 inp = inkey(win->flags & __NOTIMEOUT ? 0 : 1, win->delay);
678 break;
679 }
680 } else {
681 switch (win->delay)
682 {
683 case -1:
684 if (__delay() == ERR) {
685 __restore_termios();
686 return ERR;
687 }
688 break;
689 case 0:
690 if (__nodelay() == ERR) {
691 __restore_termios();
692 return ERR;
693 }
694 break;
695 default:
696 if (__timeout(win->delay) == ERR) {
697 __restore_termios();
698 return ERR;
699 }
700 break;
701 }
702
703 c = getchar();
704 if (_cursesi_screen->resized) {
705 if (c != -1)
706 ungetch(c);
707 _cursesi_screen->resized = 0;
708 clearerr(infd);
709 __restore_termios();
710 return KEY_RESIZE;
711 }
712 if (feof(infd)) {
713 clearerr(infd);
714 __restore_termios();
715 return ERR; /* we have timed out */
716 }
717
718 if (ferror(infd)) {
719 clearerr(infd);
720 inp = ERR;
721 } else {
722 inp = c;
723 }
724 }
725 #ifdef DEBUG
726 if (inp > 255)
727 /* we have a key symbol - treat it differently */
728 /* XXXX perhaps __unctrl should be expanded to include
729 * XXXX the keysyms in the table....
730 */
731 __CTRACE(__CTRACE_INPUT, "wgetch assembled keysym 0x%x\n", inp);
732 else
733 __CTRACE(__CTRACE_INPUT, "wgetch got '%s'\n", unctrl(inp));
734 #endif
735 if (win->delay > -1) {
736 if (__delay() == ERR) {
737 __restore_termios();
738 return ERR;
739 }
740 }
741
742 __restore_termios();
743
744 if (__echoit)
745 waddch(win, (chtype) inp);
746
747 if (weset)
748 nocbreak();
749
750 if (_cursesi_screen->nl && inp == 13)
751 inp = 10;
752
753 return ((inp < 0) || (inp == ERR) ? ERR : inp);
754 }
755
756 /*
757 * ungetch --
758 * Put the character back into the input queue.
759 */
760 int
761 ungetch(int c)
762 {
763 return ((ungetc(c, _cursesi_screen->infd) == EOF) ? ERR : OK);
764 }
765