1/* $NetBSD: getch.c,v 1.79 2024/05/14 10:22:48 uwe 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 35static char sccsid[] = "@(#)getch.c 8.2 (Berkeley) 5/4/94"; 36#else 37__RCSID("$NetBSD: getch.c,v 1.79 2024/05/14 10:22:48 uwe Exp $"); 38#endif 39#endif /* not lint */ 40 41#include <errno.h> 42#include <string.h> 43#include <stdlib.h> 44#include <unistd.h> 45#include <stdio.h> 46#include "curses.h" 47#include "curses_private.h" 48#include "keymap.h" 49 50short _cursesi_state; /* state of the inkey function */ 51 52static const struct tcdata tc[] = { 53 {TICODE_kSAV, KEY_SSAVE}, 54 {TICODE_kSPD, KEY_SSUSPEND}, 55 {TICODE_kUND, KEY_SUNDO}, 56 {TICODE_kHLP, KEY_SHELP}, 57 {TICODE_kHOM, KEY_SHOME}, 58 {TICODE_kIC, KEY_SIC}, 59 {TICODE_kLFT, KEY_SLEFT}, 60 {TICODE_krdo, KEY_REDO}, 61 {TICODE_khlp, KEY_HELP}, 62 {TICODE_kmrk, KEY_MARK}, 63 {TICODE_kmsg, KEY_MESSAGE}, 64 {TICODE_kmov, KEY_MOVE}, 65 {TICODE_knxt, KEY_NEXT}, 66 {TICODE_kopn, KEY_OPEN}, 67 {TICODE_kopt, KEY_OPTIONS}, 68 {TICODE_kprv, KEY_PREVIOUS}, 69 {TICODE_kprt, KEY_PRINT}, 70 {TICODE_kMSG, KEY_SMESSAGE}, 71 {TICODE_kMOV, KEY_SMOVE}, 72 {TICODE_kNXT, KEY_SNEXT}, 73 {TICODE_kOPT, KEY_SOPTIONS}, 74 {TICODE_kPRV, KEY_SPREVIOUS}, 75 {TICODE_kPRT, KEY_SPRINT}, 76 {TICODE_kRDO, KEY_SREDO}, 77 {TICODE_kRPL, KEY_SREPLACE}, 78 {TICODE_kRIT, KEY_SRIGHT}, 79 {TICODE_kRES, KEY_SRSUME}, 80 {TICODE_kCAN, KEY_SCANCEL}, 81 {TICODE_kref, KEY_REFERENCE}, 82 {TICODE_krfr, KEY_REFRESH}, 83 {TICODE_krpl, KEY_REPLACE}, 84 {TICODE_krst, KEY_RESTART}, 85 {TICODE_kres, KEY_RESUME}, 86 {TICODE_ksav, KEY_SAVE}, 87 {TICODE_kspd, KEY_SUSPEND}, 88 {TICODE_kund, KEY_UNDO}, 89 {TICODE_kBEG, KEY_SBEG}, 90 {TICODE_kFND, KEY_SFIND}, 91 {TICODE_kCMD, KEY_SCOMMAND}, 92 {TICODE_kCPY, KEY_SCOPY}, 93 {TICODE_kCRT, KEY_SCREATE}, 94 {TICODE_kDC, KEY_SDC}, 95 {TICODE_kDL, KEY_SDL}, 96 {TICODE_kslt, KEY_SELECT}, 97 {TICODE_kEND, KEY_SEND}, 98 {TICODE_kEOL, KEY_SEOL}, 99 {TICODE_kEXT, KEY_SEXIT}, 100 {TICODE_kfnd, KEY_FIND}, 101 {TICODE_kbeg, KEY_BEG}, 102 {TICODE_kcan, KEY_CANCEL}, 103 {TICODE_kclo, KEY_CLOSE}, 104 {TICODE_kcmd, KEY_COMMAND}, 105 {TICODE_kcpy, KEY_COPY}, 106 {TICODE_kcrt, KEY_CREATE}, 107 {TICODE_kend, KEY_END}, 108 {TICODE_kent, KEY_ENTER}, 109 {TICODE_kext, KEY_EXIT}, 110 {TICODE_kf11, KEY_F(11)}, 111 {TICODE_kf12, KEY_F(12)}, 112 {TICODE_kf13, KEY_F(13)}, 113 {TICODE_kf14, KEY_F(14)}, 114 {TICODE_kf15, KEY_F(15)}, 115 {TICODE_kf16, KEY_F(16)}, 116 {TICODE_kf17, KEY_F(17)}, 117 {TICODE_kf18, KEY_F(18)}, 118 {TICODE_kf19, KEY_F(19)}, 119 {TICODE_kf20, KEY_F(20)}, 120 {TICODE_kf21, KEY_F(21)}, 121 {TICODE_kf22, KEY_F(22)}, 122 {TICODE_kf23, KEY_F(23)}, 123 {TICODE_kf24, KEY_F(24)}, 124 {TICODE_kf25, KEY_F(25)}, 125 {TICODE_kf26, KEY_F(26)}, 126 {TICODE_kf27, KEY_F(27)}, 127 {TICODE_kf28, KEY_F(28)}, 128 {TICODE_kf29, KEY_F(29)}, 129 {TICODE_kf30, KEY_F(30)}, 130 {TICODE_kf31, KEY_F(31)}, 131 {TICODE_kf32, KEY_F(32)}, 132 {TICODE_kf33, KEY_F(33)}, 133 {TICODE_kf34, KEY_F(34)}, 134 {TICODE_kf35, KEY_F(35)}, 135 {TICODE_kf36, KEY_F(36)}, 136 {TICODE_kf37, KEY_F(37)}, 137 {TICODE_kf38, KEY_F(38)}, 138 {TICODE_kf39, KEY_F(39)}, 139 {TICODE_kf40, KEY_F(40)}, 140 {TICODE_kf41, KEY_F(41)}, 141 {TICODE_kf42, KEY_F(42)}, 142 {TICODE_kf43, KEY_F(43)}, 143 {TICODE_kf44, KEY_F(44)}, 144 {TICODE_kf45, KEY_F(45)}, 145 {TICODE_kf46, KEY_F(46)}, 146 {TICODE_kf47, KEY_F(47)}, 147 {TICODE_kf48, KEY_F(48)}, 148 {TICODE_kf49, KEY_F(49)}, 149 {TICODE_kf50, KEY_F(50)}, 150 {TICODE_kf51, KEY_F(51)}, 151 {TICODE_kf52, KEY_F(52)}, 152 {TICODE_kf53, KEY_F(53)}, 153 {TICODE_kf54, KEY_F(54)}, 154 {TICODE_kf55, KEY_F(55)}, 155 {TICODE_kf56, KEY_F(56)}, 156 {TICODE_kf57, KEY_F(57)}, 157 {TICODE_kf58, KEY_F(58)}, 158 {TICODE_kf59, KEY_F(59)}, 159 {TICODE_kf60, KEY_F(60)}, 160 {TICODE_kf61, KEY_F(61)}, 161 {TICODE_kf62, KEY_F(62)}, 162 {TICODE_kf63, KEY_F(63)}, 163 {TICODE_ka1, KEY_A1}, 164 {TICODE_kb2, KEY_B2}, 165 {TICODE_ka3, KEY_A3}, 166 {TICODE_kc1, KEY_C1}, 167 {TICODE_kc3, KEY_C3}, 168 {TICODE_kmous, KEY_MOUSE}, 169 {TICODE_kf0, KEY_F0}, 170 {TICODE_kf1, KEY_F(1)}, 171 {TICODE_kf2, KEY_F(2)}, 172 {TICODE_kf3, KEY_F(3)}, 173 {TICODE_kf4, KEY_F(4)}, 174 {TICODE_kf5, KEY_F(5)}, 175 {TICODE_kf6, KEY_F(6)}, 176 {TICODE_kf7, KEY_F(7)}, 177 {TICODE_kf8, KEY_F(8)}, 178 {TICODE_kf9, KEY_F(9)}, 179 {TICODE_kf10, KEY_F(10)}, 180 {TICODE_kil1, KEY_IL}, 181 {TICODE_ktbc, KEY_CATAB}, 182 {TICODE_kcbt, KEY_BTAB}, 183 {TICODE_kbs, KEY_BACKSPACE}, 184 {TICODE_kclr, KEY_CLEAR}, 185 {TICODE_kdch1, KEY_DC}, 186 {TICODE_kcud1, KEY_DOWN}, 187 {TICODE_kel, KEY_EOL}, 188 {TICODE_kind, KEY_SF}, 189 {TICODE_kll, KEY_LL}, 190 {TICODE_khome, KEY_HOME}, 191 {TICODE_kich1, KEY_IC}, 192 {TICODE_kdl1, KEY_DL}, 193 {TICODE_kcub1, KEY_LEFT}, 194 {TICODE_krmir, KEY_EIC}, 195 {TICODE_knp, KEY_NPAGE}, 196 {TICODE_kpp, KEY_PPAGE}, 197 {TICODE_kri, KEY_SR}, 198 {TICODE_kcuf1, KEY_RIGHT}, 199 {TICODE_ked, KEY_EOS}, 200 {TICODE_khts, KEY_STAB}, 201 {TICODE_kctab, KEY_CTAB}, 202 {TICODE_kcuu1, KEY_UP} 203}; 204/* Number of TC entries .... */ 205static const int num_tcs = (sizeof(tc) / sizeof(struct tcdata)); 206 207/* Key buffer */ 208#define INBUF_SZ 16 /* size of key buffer - must be larger than 209 * longest multi-key sequence */ 210static wchar_t inbuf[INBUF_SZ]; 211static int start, end, working; /* pointers for manipulating inbuf data */ 212 213/* prototypes for private functions */ 214static void add_key_sequence(SCREEN *screen, const char *sequence, int key_type); 215static key_entry_t *add_new_key(keymap_t *current, char ch, int key_type, 216 int symbol); 217static void delete_key_sequence(keymap_t *current, int key_type); 218static void do_keyok(keymap_t *current, int key_type, bool set, bool flag, 219 int *retval); 220static keymap_t *new_keymap(void); /* create a new keymap */ 221static key_entry_t *new_key(void); /* create a new key entry */ 222static wchar_t inkey(int to, int delay); 223 224/* 225 * Free the storage associated with the given keymap 226 */ 227void 228_cursesi_free_keymap(keymap_t *map) 229{ 230 int i; 231 232 /* check for, and free, child keymaps */ 233 for (i = 0; i < MAX_CHAR; i++) { 234 if (map->mapping[i] >= 0) { 235 if (map->key[map->mapping[i]]->type == KEYMAP_MULTI) 236 _cursesi_free_keymap( 237 map->key[map->mapping[i]]->value.next); 238 } 239 } 240 241 /* now free any allocated keymap structs */ 242 for (i = 0; i < map->count; i += KEYMAP_ALLOC_CHUNK) { 243 free(map->key[i]); 244 } 245 246 free(map->key); 247 free(map); 248} 249 250 251/* 252 * Add a new key entry to the keymap pointed to by current. Entry 253 * contains the character to add to the keymap, type is the type of 254 * entry to add (either multikey or leaf) and symbol is the symbolic 255 * value for a leaf type entry. The function returns a pointer to the 256 * new keymap entry. 257 */ 258static key_entry_t * 259add_new_key(keymap_t *current, char chr, int key_type, int symbol) 260{ 261 key_entry_t *the_key; 262 int i, ki; 263 264 __CTRACE(__CTRACE_MISC, 265 "Adding character %s of type %d, symbol 0x%x\n", 266 unctrl(chr), key_type, symbol); 267 if (current->mapping[(unsigned char)chr] < 0) { 268 if (current->mapping[(unsigned char)chr] == MAPPING_UNUSED) { 269 /* first time for this char */ 270 current->mapping[(unsigned char)chr] = 271 current->count; /* map new entry */ 272 ki = current->count; 273 274 /* make sure we have room in the key array first */ 275 if ((current->count & (KEYMAP_ALLOC_CHUNK - 1)) == 0) 276 { 277 if ((current->key = 278 realloc(current->key, 279 ki * sizeof(key_entry_t *) 280 + KEYMAP_ALLOC_CHUNK * sizeof(key_entry_t *))) == NULL) { 281 fprintf(stderr, 282 "Could not malloc for key entry\n"); 283 exit(1); 284 } 285 286 the_key = new_key(); 287 for (i = 0; i < KEYMAP_ALLOC_CHUNK; i++) { 288 current->key[ki + i] = &the_key[i]; 289 } 290 } 291 } else { 292 /* the mapping was used but freed, reuse it */ 293 ki = - current->mapping[(unsigned char) chr]; 294 current->mapping[(unsigned char) chr] = ki; 295 } 296 297 current->count++; 298 299 /* point at the current key array element to use */ 300 the_key = current->key[ki]; 301 302 the_key->type = key_type; 303 304 switch (key_type) { 305 case KEYMAP_MULTI: 306 /* need for next key */ 307 __CTRACE(__CTRACE_MISC, "Creating new keymap\n"); 308 the_key->value.next = new_keymap(); 309 the_key->enable = TRUE; 310 break; 311 312 case KEYMAP_LEAF: 313 /* the associated symbol for the key */ 314 __CTRACE(__CTRACE_MISC, "Adding leaf key\n"); 315 the_key->value.symbol = symbol; 316 the_key->enable = TRUE; 317 break; 318 319 default: 320 fprintf(stderr, "add_new_key: bad type passed\n"); 321 exit(1); 322 } 323 } else { 324 /* the key is already known - just return the address. */ 325 __CTRACE(__CTRACE_MISC, "Keymap already known\n"); 326 the_key = current->key[current->mapping[(unsigned char)chr]]; 327 } 328 329 return the_key; 330} 331 332/* 333 * Delete the given key symbol from the key mappings for the screen. 334 * 335 */ 336static void 337delete_key_sequence(keymap_t *current, int key_type) 338{ 339 key_entry_t *key; 340 int i; 341 342 /* 343 * we need to iterate over all the keys as there may be 344 * multiple instances of the leaf symbol. 345 */ 346 for (i = 0; i < MAX_CHAR; i++) { 347 if (current->mapping[i] < 0) 348 continue; /* no mapping for the key, next! */ 349 350 key = current->key[current->mapping[i]]; 351 352 if (key->type == KEYMAP_MULTI) { 353 /* have not found the leaf, recurse down */ 354 delete_key_sequence(key->value.next, key_type); 355 /* if we deleted the last key in the map, free */ 356 if (key->value.next->count == 0) 357 _cursesi_free_keymap(key->value.next); 358 } else if ((key->type == KEYMAP_LEAF) 359 && (key->value.symbol == key_type)) { 360 __CTRACE(__CTRACE_INPUT, 361 "delete_key_sequence: found keysym %d, deleting\n", 362 key_type); 363 key->enable = FALSE; 364 } 365 } 366} 367 368/* 369 * Add the sequence of characters given in sequence as the key mapping 370 * for the given key symbol. 371 */ 372static void 373add_key_sequence(SCREEN *screen, const char *sequence, int key_type) 374{ 375 key_entry_t *tmp_key; 376 keymap_t *current; 377 int length, j, key_ent; 378 379 __CTRACE(__CTRACE_MISC, "add_key_sequence: add key sequence: %s(%s)\n", 380 sequence, keyname(key_type)); 381 current = screen->base_keymap; /* always start with 382 * base keymap. */ 383 length = (int)strlen(sequence); 384 385 /* 386 * OK - we really should never get a zero length string here, either 387 * the terminfo entry is there and it has a value or we are not called 388 * at all. Unfortunately, if someone assigns a terminfo string to the 389 * ^@ value we get passed a null string which messes up our length. 390 * So, if we get a null string then just insert a leaf value in 391 * the 0th char position of the root keymap. Note that we are 392 * totally screwed if someone terminates a multichar sequence 393 * with ^@... oh well. 394 */ 395 if (length == 0) 396 length = 1; 397 398 for (j = 0; j < length - 1; j++) { 399 /* add the entry to the struct */ 400 tmp_key = add_new_key(current, sequence[j], KEYMAP_MULTI, 0); 401 402 /* index into the key array - it's 403 clearer if we stash this */ 404 key_ent = current->mapping[(unsigned char) sequence[j]]; 405 406 current->key[key_ent] = tmp_key; 407 408 /* next key uses this map... */ 409 current = current->key[key_ent]->value.next; 410 } 411 412 /* 413 * This is the last key in the sequence (it may have been the 414 * only one but that does not matter) this means it is a leaf 415 * key and should have a symbol associated with it. 416 */ 417 tmp_key = add_new_key(current, sequence[length - 1], KEYMAP_LEAF, 418 key_type); 419 current->key[current->mapping[(int)sequence[length - 1]]] = tmp_key; 420} 421 422/* 423 * Init_getch - initialise all the pointers & structures needed to make 424 * getch work in keypad mode. 425 * 426 */ 427void 428__init_getch(SCREEN *screen) 429{ 430 char entry[1024], *p; 431 const char *s; 432 int i; 433 size_t limit, l; 434#ifdef DEBUG 435 int k, length; 436#endif 437 438 /* init the inkey state variable */ 439 _cursesi_state = INKEY_NORM; 440 441 /* init the base keymap */ 442 screen->base_keymap = new_keymap(); 443 444 /* key input buffer pointers */ 445 start = end = working = 0; 446 447 /* now do the terminfo snarfing ... */ 448 449 for (i = 0; i < num_tcs; i++) { 450 p = entry; 451 limit = 1023; 452 s = screen->term->strs[tc[i].code]; 453 if (s == NULL) 454 continue; 455 l = strlen(s) + 1; 456 if (limit < l) 457 continue; 458 strlcpy(p, s, limit); 459 p += l; 460 limit -= l; 461#ifdef DEBUG 462 __CTRACE(__CTRACE_INIT, 463 "Processing terminfo entry %d, sequence ", 464 tc[i].code); 465 length = (int) strlen(entry); 466 for (k = 0; k <= length -1; k++) 467 __CTRACE(__CTRACE_INIT, "%s", unctrl(entry[k])); 468 __CTRACE(__CTRACE_INIT, "\n"); 469#endif 470 add_key_sequence(screen, entry, tc[i].symbol); 471 } 472} 473 474 475/* 476 * new_keymap - allocates & initialises a new keymap structure. This 477 * function returns a pointer to the new keymap. 478 * 479 */ 480static keymap_t * 481new_keymap(void) 482{ 483 int i; 484 keymap_t *new_map; 485 486 if ((new_map = malloc(sizeof(keymap_t))) == NULL) { 487 perror("Inkey: Cannot allocate new keymap"); 488 exit(2); 489 } 490 491 /* Initialise the new map */ 492 new_map->count = 0; 493 for (i = 0; i < MAX_CHAR; i++) { 494 new_map->mapping[i] = MAPPING_UNUSED; /* no mapping for char */ 495 } 496 497 /* key array will be allocated when first key is added */ 498 new_map->key = NULL; 499 500 return new_map; 501} 502 503/* 504 * new_key - allocates & initialises a new key entry. This function returns 505 * a pointer to the newly allocated key entry. 506 * 507 */ 508static key_entry_t * 509new_key(void) 510{ 511 key_entry_t *new_one; 512 int i; 513 514 new_one = malloc(KEYMAP_ALLOC_CHUNK * sizeof(key_entry_t)); 515 if (new_one == NULL) { 516 perror("inkey: Cannot allocate new key entry chunk"); 517 exit(2); 518 } 519 520 for (i = 0; i < KEYMAP_ALLOC_CHUNK; i++) { 521 new_one[i].type = 0; 522 new_one[i].value.next = NULL; 523 } 524 525 return new_one; 526} 527 528/* 529 * inkey - do the work to process keyboard input, check for multi-key 530 * sequences and return the appropriate symbol if we get a match. 531 * 532 */ 533 534static wchar_t 535inkey(int to, int delay) 536{ 537 wchar_t k; 538 int c, mapping; 539 keymap_t *current = _cursesi_screen->base_keymap; 540 FILE *infd = _cursesi_screen->infd; 541 542 k = 0; /* XXX gcc -Wuninitialized */ 543 544 __CTRACE(__CTRACE_INPUT, "inkey (%d, %d)\n", to, delay); 545 for (;;) { /* loop until we get a complete key sequence */ 546reread: 547 if (_cursesi_state == INKEY_NORM) { 548 if (delay && __timeout(delay) == ERR) 549 return ERR; 550 c = __fgetc_resize(infd); 551 if (c == ERR || c == KEY_RESIZE) { 552 clearerr(infd); 553 return c; 554 } 555 556 if (delay && (__notimeout() == ERR)) 557 return ERR; 558 559 k = (wchar_t)c; 560 __CTRACE(__CTRACE_INPUT, 561 "inkey (state normal) got '%s'\n", unctrl(k)); 562 563 working = start; 564 inbuf[working] = k; 565 INC_POINTER(working); 566 end = working; 567 568 /* go to the assembling state now */ 569 _cursesi_state = INKEY_ASSEMBLING; 570 571 } else if (_cursesi_state == INKEY_BACKOUT) { 572 k = inbuf[working]; 573 INC_POINTER(working); 574 if (working == end) { /* see if we have run 575 * out of keys in the 576 * backlog */ 577 578 /* if we have then switch to assembling */ 579 _cursesi_state = INKEY_ASSEMBLING; 580 } 581 } else if (_cursesi_state == INKEY_ASSEMBLING) { 582 /* assembling a key sequence */ 583 if (delay) { 584 if (__timeout(to ? (ESCDELAY / 100) : delay) 585 == ERR) 586 return ERR; 587 } else { 588 if (to && (__timeout(ESCDELAY / 100) == ERR)) 589 return ERR; 590 } 591 592 c = __fgetc_resize(infd); 593 if (ferror(infd)) { 594 clearerr(infd); 595 return c; 596 } 597 598 if ((to || delay) && (__notimeout() == ERR)) 599 return ERR; 600 601 __CTRACE(__CTRACE_INPUT, 602 "inkey (state assembling) got '%s'\n", unctrl(k)); 603 if (feof(infd) || c == -1) { /* inter-char timeout, 604 * start backing out */ 605 clearerr(infd); 606 if (start == end) 607 /* no chars in the buffer, restart */ 608 goto reread; 609 610 k = inbuf[start]; 611 _cursesi_state = INKEY_TIMEOUT; 612 } else { 613 k = (wchar_t) c; 614 inbuf[working] = k; 615 INC_POINTER(working); 616 end = working; 617 } 618 } else { 619 fprintf(stderr, "Inkey state screwed - exiting!!!"); 620 exit(2); 621 } 622 623 /* 624 * Check key has no special meaning and we have not 625 * timed out and the key has not been disabled 626 */ 627 mapping = current->mapping[k]; 628 if (((_cursesi_state == INKEY_TIMEOUT) || (mapping < 0)) 629 || ((current->key[mapping]->type == KEYMAP_LEAF) 630 && (current->key[mapping]->enable == FALSE))) { 631 /* return the first key we know about */ 632 k = inbuf[start]; 633 634 INC_POINTER(start); 635 working = start; 636 637 if (start == end) { /* only one char processed */ 638 _cursesi_state = INKEY_NORM; 639 } else {/* otherwise we must have more than one char 640 * to backout */ 641 _cursesi_state = INKEY_BACKOUT; 642 } 643 return k; 644 } else { /* must be part of a multikey sequence */ 645 /* check for completed key sequence */ 646 if (current->key[current->mapping[k]]->type == KEYMAP_LEAF) { 647 start = working; /* eat the key sequence 648 * in inbuf */ 649 650 /* check if inbuf empty now */ 651 if (start == end) { 652 /* if it is go back to normal */ 653 _cursesi_state = INKEY_NORM; 654 } else { 655 /* otherwise go to backout state */ 656 _cursesi_state = INKEY_BACKOUT; 657 } 658 659 /* return the symbol */ 660 return current->key[current->mapping[k]]->value.symbol; 661 662 } else { 663 /* 664 * Step on to next part of the multi-key 665 * sequence. 666 */ 667 current = current->key[current->mapping[k]]->value.next; 668 } 669 } 670 } 671} 672 673#ifndef _CURSES_USE_MACROS 674/* 675 * getch -- 676 * Read in a character from stdscr. 677 */ 678int 679getch(void) 680{ 681 return wgetch(stdscr); 682} 683 684/* 685 * mvgetch -- 686 * Read in a character from stdscr at the given location. 687 */ 688int 689mvgetch(int y, int x) 690{ 691 return mvwgetch(stdscr, y, x); 692} 693 694/* 695 * mvwgetch -- 696 * Read in a character from stdscr at the given location in the 697 * given window. 698 */ 699int 700mvwgetch(WINDOW *win, int y, int x) 701{ 702 if (wmove(win, y, x) == ERR) 703 return ERR; 704 705 return wgetch(win); 706} 707 708#endif 709 710/* 711 * keyok -- 712 * Set the enable flag for a keysym, if the flag is false then 713 * getch will not return this keysym even if the matching key sequence 714 * is seen. 715 */ 716int 717keyok(int key_type, bool flag) 718{ 719 int result = ERR; 720 721 if (_cursesi_screen != NULL) 722 do_keyok(_cursesi_screen->base_keymap, key_type, 723 true, flag, &result); 724 return result; 725} 726 727/* 728 * do_keyok -- 729 * Does the actual work for keyok, we need to recurse through the 730 * keymaps finding the passed key symbol. 731 */ 732static void 733do_keyok(keymap_t *current, int key_type, bool set, bool flag, int *retval) 734{ 735 key_entry_t *key; 736 int i; 737 738 /* 739 * we need to iterate over all the keys as there may be 740 * multiple instances of the leaf symbol. 741 */ 742 for (i = 0; i < MAX_CHAR; i++) { 743 if (current->mapping[i] < 0) 744 continue; /* no mapping for the key, next! */ 745 746 key = current->key[current->mapping[i]]; 747 748 if (key->type == KEYMAP_MULTI) 749 do_keyok(key->value.next, key_type, set, flag, retval); 750 else if ((key->type == KEYMAP_LEAF) 751 && (key->value.symbol == key_type)) { 752 if (set) 753 key->enable = flag; 754 *retval = OK; /* we found at least one instance, ok */ 755 } 756 } 757} 758 759/* 760 * define_key -- 761 * Add a custom mapping of a key sequence to key symbol. 762 * 763 */ 764int 765define_key(const char *sequence, int symbol) 766{ 767 768 if (symbol <= 0 || _cursesi_screen == NULL) 769 return ERR; 770 771 if (sequence == NULL) { 772 __CTRACE(__CTRACE_INPUT, "define_key: deleting keysym %d\n", 773 symbol); 774 delete_key_sequence(_cursesi_screen->base_keymap, symbol); 775 } else 776 add_key_sequence(_cursesi_screen, sequence, symbol); 777 778 return OK; 779} 780 781/* 782 * wgetch -- 783 * Read in a character from the window. 784 */ 785int 786wgetch(WINDOW *win) 787{ 788 int inp, weset; 789 int c; 790 FILE *infd = _cursesi_screen->infd; 791 792 __CTRACE(__CTRACE_INPUT, "wgetch: win(%p)\n", win); 793 if (win == NULL) 794 return ERR; 795 if (!(win->flags & __SCROLLOK) && (win->flags & __FULLWIN) 796 && win->curx == win->maxx - 1 && win->cury == win->maxy - 1 797 && __echoit) 798 return ERR; 799 800 if (!(win->flags & __ISPAD)) { 801 if (is_wintouched(win)) 802 wrefresh(win); 803 else if (__echoit && ((_cursesi_screen->curscr->cury != (win->begy + win->cury)) 804 || (_cursesi_screen->curscr->curx != (win->begx + win->curx)))) { 805 __CTRACE(__CTRACE_INPUT, 806 "wgetch: curscr cury %d cury %d " 807 "curscr curx %d curx %d\n", 808 _cursesi_screen->curscr->cury, 809 win->begy + win->cury, 810 _cursesi_screen->curscr->curx, 811 win->begx + win->curx); 812 /* 813 * Just in case the window is not dirty but the 814 * cursor was moved, check and update the 815 * cursor location. 816 */ 817 mvcur(_cursesi_screen->curscr->cury, 818 _cursesi_screen->curscr->curx, 819 win->cury + win->begy, win->curx + win->begx); 820 _cursesi_screen->curscr->cury = 821 win->cury + win->begy; 822 _cursesi_screen->curscr->curx = 823 win->curx + win->begx; 824 } 825 } 826 827 __CTRACE(__CTRACE_INPUT, "wgetch: __echoit = %d, " 828 "__rawmode = %d, __nl = %d, flags = %#.4x, delay = %d\n", 829 __echoit, __rawmode, _cursesi_screen->nl, win->flags, win->delay); 830 if (_cursesi_screen->resized) { 831 resizeterm(LINES, COLS); 832 _cursesi_screen->resized = 0; 833 __CTRACE(__CTRACE_INPUT, "wgetch returning KEY_RESIZE\n"); 834 return KEY_RESIZE; 835 } 836 if (_cursesi_screen->unget_pos) { 837 __CTRACE(__CTRACE_INPUT, "wgetch returning char at %d\n", 838 _cursesi_screen->unget_pos); 839 _cursesi_screen->unget_pos--; 840 c = _cursesi_screen->unget_list[_cursesi_screen->unget_pos]; 841 if (__echoit) 842 waddch(win, (chtype) c); 843 return c; 844 } 845 if (__echoit && !__rawmode) { 846 cbreak(); 847 weset = 1; 848 } else 849 weset = 0; 850 851 __save_termios(); 852 853 if (win->flags & __KEYPAD) { 854 switch (win->delay) { 855 case -1: 856 inp = inkey (win->flags & __NOTIMEOUT ? 0 : 1, 0); 857 break; 858 case 0: 859 if (__nodelay() == ERR) 860 return ERR; 861 inp = inkey(0, 0); 862 break; 863 default: 864 inp = inkey(win->flags & __NOTIMEOUT ? 0 : 1, win->delay); 865 break; 866 } 867 } else { 868 switch (win->delay) { 869 case -1: 870 if (__delay() == ERR) 871 return ERR; 872 break; 873 case 0: 874 if (__nodelay() == ERR) 875 return ERR; 876 break; 877 default: 878 if (__timeout(win->delay) == ERR) 879 return ERR; 880 break; 881 } 882 883 inp = __fgetc_resize(infd); 884 if (inp == ERR || inp == KEY_RESIZE) { 885 clearerr(infd); 886 __restore_termios(); 887 return inp; 888 } 889 } 890#ifdef DEBUG 891 if (inp > 255) 892 /* we have a key symbol - treat it differently */ 893 /* XXXX perhaps __unctrl should be expanded to include 894 * XXXX the keysyms in the table.... 895 */ 896 __CTRACE(__CTRACE_INPUT, "wgetch assembled keysym 0x%x\n", inp); 897 else 898 __CTRACE(__CTRACE_INPUT, "wgetch got '%s'\n", unctrl(inp)); 899#endif 900 if (win->delay > -1) { 901 if (__delay() == ERR) 902 return ERR; 903 } 904 905 __restore_termios(); 906 907 if ((__echoit) && (inp < KEY_MIN)) 908 waddch(win, (chtype) inp); 909 910 if (weset) 911 nocbreak(); 912 913 if (_cursesi_screen->nl && inp == 13) 914 inp = 10; 915 916 return ((inp < 0) || (inp == ERR) ? ERR : inp); 917} 918 919/* 920 * ungetch -- 921 * Put the character back into the input queue. 922 */ 923int 924ungetch(int c) 925{ 926 return __unget((wint_t)c); 927} 928 929/* 930 * __unget -- 931 * Do the work for ungetch() and unget_wch(); 932 */ 933int 934__unget(wint_t c) 935{ 936 wchar_t *p; 937 int len; 938 939 __CTRACE(__CTRACE_INPUT, "__unget(%x)\n", c); 940 if (_cursesi_screen == NULL) 941 return ERR; 942 if (_cursesi_screen->unget_pos >= _cursesi_screen->unget_len) { 943 len = _cursesi_screen->unget_len + 32; 944 if ((p = realloc(_cursesi_screen->unget_list, 945 sizeof(wchar_t) * len)) == NULL) { 946 /* Can't realloc(), so just lose the oldest entry */ 947 memmove(_cursesi_screen->unget_list, 948 _cursesi_screen->unget_list + sizeof(wchar_t), 949 _cursesi_screen->unget_len - 1); 950 _cursesi_screen->unget_list[_cursesi_screen->unget_len 951 - 1] = c; 952 _cursesi_screen->unget_pos = 953 _cursesi_screen->unget_len; 954 return OK; 955 } else { 956 _cursesi_screen->unget_pos = 957 _cursesi_screen->unget_len; 958 _cursesi_screen->unget_len = len; 959 _cursesi_screen->unget_list = p; 960 } 961 } 962 _cursesi_screen->unget_list[_cursesi_screen->unget_pos] = c; 963 _cursesi_screen->unget_pos++; 964 return OK; 965} 966 967int 968has_key(int key_type) 969{ 970 int result = ERR; 971 972 if (_cursesi_screen != NULL) 973 do_keyok(_cursesi_screen->base_keymap, key_type, 974 false, false, &result); 975 return result; 976} 977 978/* 979 * set_escdelay -- 980 * Sets the escape delay for the current screen. 981 */ 982int 983set_escdelay(int escdelay) 984{ 985 986 if (_cursesi_screen == NULL) 987 return ERR; 988 _cursesi_screen->ESCDELAY = escdelay; 989 ESCDELAY = escdelay; 990 return OK; 991} 992 993/* 994 * __fgetc_resize -- 995 * Any call to fgetc(3) should use this function instead 996 * and test for the return value of KEY_RESIZE as well as ERR. 997 */ 998int 999__fgetc_resize(FILE *infd) 1000{ 1001 int c; 1002 1003 c = fgetc(infd); 1004 if (c != EOF) 1005 return c; 1006 1007 if (!ferror(infd) || errno != EINTR || !_cursesi_screen->resized) 1008 return ERR; 1009 __CTRACE(__CTRACE_INPUT, "__fgetc_resize returning KEY_RESIZE\n"); 1010 resizeterm(LINES, COLS); 1011 _cursesi_screen->resized = 0; 1012 return KEY_RESIZE; 1013} 1014