1 /* $OpenBSD: lib_getch.c,v 1.12 2023/10/17 09:52:08 nicm Exp $ */ 2 3 /**************************************************************************** 4 * Copyright 2018-2022,2023 Thomas E. Dickey * 5 * Copyright 1998-2015,2016 Free Software Foundation, Inc. * 6 * * 7 * Permission is hereby granted, free of charge, to any person obtaining a * 8 * copy of this software and associated documentation files (the * 9 * "Software"), to deal in the Software without restriction, including * 10 * without limitation the rights to use, copy, modify, merge, publish, * 11 * distribute, distribute with modifications, sublicense, and/or sell * 12 * copies of the Software, and to permit persons to whom the Software is * 13 * furnished to do so, subject to the following conditions: * 14 * * 15 * The above copyright notice and this permission notice shall be included * 16 * in all copies or substantial portions of the Software. * 17 * * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 21 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 24 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 25 * * 26 * Except as contained in this notice, the name(s) of the above copyright * 27 * holders shall not be used in advertising or otherwise to promote the * 28 * sale, use or other dealings in this Software without prior written * 29 * authorization. * 30 ****************************************************************************/ 31 32 /**************************************************************************** 33 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 34 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 35 * and: Thomas E. Dickey 1996-on * 36 * and: Juergen Pfeifer 2009 * 37 ****************************************************************************/ 38 39 /* 40 ** lib_getch.c 41 ** 42 ** The routine getch(). 43 ** 44 */ 45 46 #define NEED_KEY_EVENT 47 #include <curses.priv.h> 48 49 MODULE_ID("$Id: lib_getch.c,v 1.12 2023/10/17 09:52:08 nicm Exp $") 50 51 #include <fifo_defs.h> 52 53 #if USE_REENTRANT 54 #define GetEscdelay(sp) *_nc_ptr_Escdelay(sp) 55 NCURSES_EXPORT(int) 56 NCURSES_PUBLIC_VAR(ESCDELAY) (void) 57 { 58 return *(_nc_ptr_Escdelay(CURRENT_SCREEN)); 59 } 60 61 NCURSES_EXPORT(int *) 62 _nc_ptr_Escdelay(SCREEN *sp) 63 { 64 return ptrEscdelay(sp); 65 } 66 #else 67 #define GetEscdelay(sp) ESCDELAY 68 NCURSES_EXPORT_VAR(int) ESCDELAY = 1000; 69 #endif 70 71 #if NCURSES_EXT_FUNCS 72 NCURSES_EXPORT(int) 73 NCURSES_SP_NAME(set_escdelay) (NCURSES_SP_DCLx int value) 74 { 75 int code = OK; 76 if (value < 0) { 77 code = ERR; 78 } else { 79 #if USE_REENTRANT 80 if (SP_PARM) { 81 SET_ESCDELAY(value); 82 } else { 83 code = ERR; 84 } 85 #else 86 (void) SP_PARM; 87 ESCDELAY = value; 88 #endif 89 } 90 return code; 91 } 92 93 #if NCURSES_SP_FUNCS 94 NCURSES_EXPORT(int) 95 set_escdelay(int value) 96 { 97 int code; 98 if (value < 0) { 99 code = ERR; 100 } else { 101 #if USE_REENTRANT 102 code = NCURSES_SP_NAME(set_escdelay) (CURRENT_SCREEN, value); 103 #else 104 ESCDELAY = value; 105 code = OK; 106 #endif 107 } 108 return code; 109 } 110 #endif 111 #endif /* NCURSES_EXT_FUNCS */ 112 113 #if NCURSES_EXT_FUNCS 114 NCURSES_EXPORT(int) 115 NCURSES_SP_NAME(get_escdelay) (NCURSES_SP_DCL0) 116 { 117 #if !USE_REENTRANT 118 (void) SP_PARM; 119 #endif 120 return GetEscdelay(SP_PARM); 121 } 122 123 #if NCURSES_SP_FUNCS 124 NCURSES_EXPORT(int) 125 get_escdelay(void) 126 { 127 return NCURSES_SP_NAME(get_escdelay) (CURRENT_SCREEN); 128 } 129 #endif 130 #endif /* NCURSES_EXT_FUNCS */ 131 132 static int 133 _nc_use_meta(WINDOW *win) 134 { 135 SCREEN *sp = _nc_screen_of(win); 136 return (sp ? sp->_use_meta : 0); 137 } 138 139 #ifdef USE_TERM_DRIVER 140 # if defined(_NC_WINDOWS) && !defined(EXP_WIN32_DRIVER) 141 static HANDLE 142 _nc_get_handle(int fd) 143 { 144 intptr_t value = _get_osfhandle(fd); 145 return (HANDLE) value; 146 } 147 # endif 148 #endif 149 150 /* 151 * Check for mouse activity, returning nonzero if we find any. 152 */ 153 static int 154 check_mouse_activity(SCREEN *sp, int delay EVENTLIST_2nd(_nc_eventlist * evl)) 155 { 156 int rc; 157 158 #ifdef USE_TERM_DRIVER 159 TERMINAL_CONTROL_BLOCK *TCB = TCBOf(sp); 160 rc = TCBOf(sp)->drv->td_testmouse(TCBOf(sp), delay EVENTLIST_2nd(evl)); 161 # if defined(EXP_WIN32_DRIVER) 162 /* if we emulate terminfo on console, we have to use the console routine */ 163 if (IsTermInfoOnConsole(sp)) { 164 rc = _nc_console_testmouse(sp, 165 _nc_console_handle(sp->_ifd), 166 delay EVENTLIST_2nd(evl)); 167 } else 168 # elif defined(_NC_WINDOWS) 169 /* if we emulate terminfo on console, we have to use the console routine */ 170 if (IsTermInfoOnConsole(sp)) { 171 HANDLE fd = _nc_get_handle(sp->_ifd); 172 rc = _nc_mingw_testmouse(sp, fd, delay EVENTLIST_2nd(evl)); 173 } else 174 # endif 175 rc = TCB->drv->td_testmouse(TCB, delay EVENTLIST_2nd(evl)); 176 #else /* !USE_TERM_DRIVER */ 177 # if USE_SYSMOUSE 178 if ((sp->_mouse_type == M_SYSMOUSE) 179 && (sp->_sysmouse_head < sp->_sysmouse_tail)) { 180 rc = TW_MOUSE; 181 } else 182 # endif 183 { 184 # if defined(EXP_WIN32_DRIVER) 185 rc = _nc_console_testmouse(sp, 186 _nc_console_handle(sp->_ifd), 187 delay 188 EVENTLIST_2nd(evl)); 189 # else 190 rc = _nc_timed_wait(sp, 191 TWAIT_MASK, 192 delay, 193 (int *) 0 194 EVENTLIST_2nd(evl)); 195 # endif 196 # if USE_SYSMOUSE 197 if ((sp->_mouse_type == M_SYSMOUSE) 198 && (sp->_sysmouse_head < sp->_sysmouse_tail) 199 && (rc == 0) 200 && (errno == EINTR)) { 201 rc |= TW_MOUSE; 202 } 203 # endif 204 } 205 #endif /* USE_TERM_DRIVER */ 206 return rc; 207 } 208 209 static NCURSES_INLINE int 210 fifo_peek(SCREEN *sp) 211 { 212 int ch = (peek >= 0) ? sp->_fifo[peek] : ERR; 213 TR(TRACE_IEVENT, ("peeking at %d", peek)); 214 215 p_inc(); 216 return ch; 217 } 218 219 static NCURSES_INLINE int 220 fifo_pull(SCREEN *sp) 221 { 222 int ch = (head >= 0) ? sp->_fifo[head] : ERR; 223 224 TR(TRACE_IEVENT, ("pulling %s from %d", _nc_tracechar(sp, ch), head)); 225 226 if (peek == head) { 227 h_inc(); 228 peek = head; 229 } else { 230 h_inc(); 231 } 232 233 #ifdef TRACE 234 if (USE_TRACEF(TRACE_IEVENT)) { 235 _nc_fifo_dump(sp); 236 _nc_unlock_global(tracef); 237 } 238 #endif 239 return ch; 240 } 241 242 static NCURSES_INLINE int 243 fifo_push(SCREEN *sp EVENTLIST_2nd(_nc_eventlist * evl)) 244 { 245 int n; 246 int ch = 0; 247 int mask = 0; 248 249 (void) mask; 250 if (tail < 0) 251 return ERR; 252 253 #ifdef NCURSES_WGETCH_EVENTS 254 if (evl 255 #if USE_GPM_SUPPORT || USE_EMX_MOUSE || USE_SYSMOUSE 256 || (sp->_mouse_fd >= 0) 257 #endif 258 ) { 259 mask = check_mouse_activity(sp, -1 EVENTLIST_2nd(evl)); 260 } else 261 mask = 0; 262 263 if (mask & TW_EVENT) { 264 T(("fifo_push: ungetch KEY_EVENT")); 265 safe_ungetch(sp, KEY_EVENT); 266 return KEY_EVENT; 267 } 268 #elif USE_GPM_SUPPORT || USE_EMX_MOUSE || USE_SYSMOUSE 269 if (sp->_mouse_fd >= 0) { 270 mask = check_mouse_activity(sp, -1 EVENTLIST_2nd(evl)); 271 } 272 #endif 273 274 #if USE_GPM_SUPPORT || USE_EMX_MOUSE 275 if ((sp->_mouse_fd >= 0) && (mask & TW_MOUSE)) { 276 sp->_mouse_event(sp); 277 ch = KEY_MOUSE; 278 n = 1; 279 } else 280 #endif 281 #if USE_SYSMOUSE 282 if ((sp->_mouse_type == M_SYSMOUSE) 283 && (sp->_sysmouse_head < sp->_sysmouse_tail)) { 284 sp->_mouse_event(sp); 285 ch = KEY_MOUSE; 286 n = 1; 287 } else if ((sp->_mouse_type == M_SYSMOUSE) 288 && (mask <= 0) && errno == EINTR) { 289 sp->_mouse_event(sp); 290 ch = KEY_MOUSE; 291 n = 1; 292 } else 293 #endif 294 #ifdef USE_TERM_DRIVER 295 if ((sp->_mouse_type == M_TERM_DRIVER) 296 && (sp->_drv_mouse_head < sp->_drv_mouse_tail)) { 297 sp->_mouse_event(sp); 298 ch = KEY_MOUSE; 299 n = 1; 300 } else 301 #endif 302 #if USE_KLIBC_KBD 303 if (NC_ISATTY(sp->_ifd) && IsCbreak(sp)) { 304 ch = _read_kbd(0, 1, !IsRaw(sp)); 305 n = (ch == -1) ? -1 : 1; 306 sp->_extended_key = (ch == 0); 307 } else 308 #endif 309 { /* Can block... */ 310 #if defined(USE_TERM_DRIVER) 311 int buf; 312 # if defined(EXP_WIN32_DRIVER) 313 if (NC_ISATTY(sp->_ifd) && IsTermInfoOnConsole(sp) && IsCbreak(sp)) { 314 _nc_set_read_thread(TRUE); 315 n = _nc_console_read(sp, 316 _nc_console_handle(sp->_ifd), 317 &buf); 318 _nc_set_read_thread(FALSE); 319 } else 320 # elif defined(_NC_WINDOWS) 321 if (NC_ISATTY(sp->_ifd) && IsTermInfoOnConsole(sp) && IsCbreak(sp)) 322 n = _nc_mingw_console_read(sp, 323 _nc_get_handle(sp->_ifd), 324 &buf); 325 else 326 # endif /* EXP_WIN32_DRIVER */ 327 n = CallDriver_1(sp, td_read, &buf); 328 ch = buf; 329 #else /* !USE_TERM_DRIVER */ 330 #if defined(EXP_WIN32_DRIVER) 331 int buf; 332 #endif 333 unsigned char c2 = 0; 334 335 _nc_set_read_thread(TRUE); 336 #if defined(EXP_WIN32_DRIVER) 337 n = _nc_console_read(sp, 338 _nc_console_handle(sp->_ifd), 339 &buf); 340 c2 = buf; 341 #else 342 n = (int) read(sp->_ifd, &c2, (size_t) 1); 343 #endif 344 _nc_set_read_thread(FALSE); 345 ch = c2; 346 #endif /* USE_TERM_DRIVER */ 347 } 348 349 if ((n == -1) || (n == 0)) { 350 TR(TRACE_IEVENT, ("read(%d,&ch,1)=%d, errno=%d", sp->_ifd, n, errno)); 351 ch = ERR; 352 } 353 TR(TRACE_IEVENT, ("read %d characters", n)); 354 355 sp->_fifo[tail] = ch; 356 sp->_fifohold = 0; 357 if (head == -1) 358 head = peek = tail; 359 t_inc(); 360 TR(TRACE_IEVENT, ("pushed %s at %d", _nc_tracechar(sp, ch), tail)); 361 #ifdef TRACE 362 if (USE_TRACEF(TRACE_IEVENT)) { 363 _nc_fifo_dump(sp); 364 _nc_unlock_global(tracef); 365 } 366 #endif 367 return ch; 368 } 369 370 static NCURSES_INLINE void 371 fifo_clear(SCREEN *sp) 372 { 373 memset(sp->_fifo, 0, sizeof(sp->_fifo)); 374 head = -1; 375 tail = peek = 0; 376 } 377 378 static int kgetch(SCREEN *, bool EVENTLIST_2nd(_nc_eventlist *)); 379 380 static void 381 recur_wrefresh(WINDOW *win) 382 { 383 #ifdef USE_PTHREADS 384 SCREEN *sp = _nc_screen_of(win); 385 bool same_sp; 386 387 if (_nc_use_pthreads) { 388 _nc_lock_global(curses); 389 same_sp = (sp == CURRENT_SCREEN); 390 _nc_unlock_global(curses); 391 } else { 392 same_sp = (sp == CURRENT_SCREEN); 393 } 394 395 if (_nc_use_pthreads && !same_sp) { 396 SCREEN *save_SP; 397 398 /* temporarily switch to the window's screen to check/refresh */ 399 _nc_lock_global(curses); 400 save_SP = CURRENT_SCREEN; 401 _nc_set_screen(sp); 402 recur_wrefresh(win); 403 _nc_set_screen(save_SP); 404 _nc_unlock_global(curses); 405 } else 406 #endif 407 if ((is_wintouched(win) || (win->_flags & _HASMOVED)) 408 && !IS_PAD(win)) { 409 wrefresh(win); 410 } 411 } 412 413 static int 414 recur_wgetnstr(WINDOW *win, char *buf) 415 { 416 SCREEN *sp = _nc_screen_of(win); 417 int rc; 418 419 if (sp != 0) { 420 #ifdef USE_PTHREADS 421 if (_nc_use_pthreads && sp != CURRENT_SCREEN) { 422 SCREEN *save_SP; 423 424 /* temporarily switch to the window's screen to get cooked input */ 425 _nc_lock_global(curses); 426 save_SP = CURRENT_SCREEN; 427 _nc_set_screen(sp); 428 rc = recur_wgetnstr(win, buf); 429 _nc_set_screen(save_SP); 430 _nc_unlock_global(curses); 431 } else 432 #endif 433 { 434 sp->_called_wgetch = TRUE; 435 rc = wgetnstr(win, buf, MAXCOLUMNS); 436 sp->_called_wgetch = FALSE; 437 } 438 } else { 439 rc = ERR; 440 } 441 return rc; 442 } 443 444 NCURSES_EXPORT(int) 445 _nc_wgetch(WINDOW *win, 446 int *result, 447 int use_meta 448 EVENTLIST_2nd(_nc_eventlist * evl)) 449 { 450 SCREEN *sp; 451 int ch; 452 int rc = 0; 453 #ifdef NCURSES_WGETCH_EVENTS 454 int event_delay = -1; 455 #endif 456 457 T((T_CALLED("_nc_wgetch(%p)"), (void *) win)); 458 459 *result = 0; 460 461 sp = _nc_screen_of(win); 462 if (win == 0 || sp == 0) { 463 returnCode(ERR); 464 } 465 466 if (cooked_key_in_fifo()) { 467 recur_wrefresh(win); 468 *result = fifo_pull(sp); 469 returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK); 470 } 471 #ifdef NCURSES_WGETCH_EVENTS 472 if (evl && (evl->count == 0)) 473 evl = NULL; 474 event_delay = _nc_eventlist_timeout(evl); 475 #endif 476 477 /* 478 * Handle cooked mode. Grab a string from the screen, 479 * stuff its contents in the FIFO queue, and pop off 480 * the first character to return it. 481 */ 482 if (head == -1 && 483 !sp->_notty && 484 !IsRaw(sp) && 485 !IsCbreak(sp) && 486 !sp->_called_wgetch) { 487 char buf[MAXCOLUMNS], *bufp; 488 489 TR(TRACE_IEVENT, ("filling queue in cooked mode")); 490 491 /* ungetch in reverse order */ 492 #ifdef NCURSES_WGETCH_EVENTS 493 rc = recur_wgetnstr(win, buf); 494 if (rc != KEY_EVENT && rc != ERR) 495 safe_ungetch(sp, '\n'); 496 #else 497 if (recur_wgetnstr(win, buf) != ERR) 498 safe_ungetch(sp, '\n'); 499 #endif 500 for (bufp = buf + strlen(buf); bufp > buf; bufp--) 501 safe_ungetch(sp, bufp[-1]); 502 503 #ifdef NCURSES_WGETCH_EVENTS 504 /* Return it first */ 505 if (rc == KEY_EVENT) { 506 *result = rc; 507 } else 508 #endif 509 *result = fifo_pull(sp); 510 returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK); 511 } 512 513 if (win->_use_keypad != sp->_keypad_on) 514 _nc_keypad(sp, win->_use_keypad); 515 516 recur_wrefresh(win); 517 518 if (win->_notimeout || (win->_delay >= 0) || (IsCbreak(sp) > 1)) { 519 if (head == -1) { /* fifo is empty */ 520 int delay; 521 522 TR(TRACE_IEVENT, ("timed delay in wgetch()")); 523 if (IsCbreak(sp) > 1) 524 delay = (IsCbreak(sp) - 1) * 100; 525 else 526 delay = win->_delay; 527 528 #ifdef NCURSES_WGETCH_EVENTS 529 if (event_delay >= 0 && delay > event_delay) 530 delay = event_delay; 531 #endif 532 533 TR(TRACE_IEVENT, ("delay is %d milliseconds", delay)); 534 535 rc = check_mouse_activity(sp, delay EVENTLIST_2nd(evl)); 536 537 #ifdef NCURSES_WGETCH_EVENTS 538 if (rc & TW_EVENT) { 539 *result = KEY_EVENT; 540 returnCode(KEY_CODE_YES); 541 } 542 #endif 543 if (!rc) { 544 goto check_sigwinch; 545 } 546 } 547 /* else go on to read data available */ 548 } 549 550 if (win->_use_keypad) { 551 /* 552 * This is tricky. We only want to get special-key 553 * events one at a time. But we want to accumulate 554 * mouse events until either (a) the mouse logic tells 555 * us it has picked up a complete gesture, or (b) 556 * there's a detectable time lapse after one. 557 * 558 * Note: if the mouse code starts failing to compose 559 * press/release events into clicks, you should probably 560 * increase the wait with mouseinterval(). 561 */ 562 int runcount = 0; 563 564 do { 565 ch = kgetch(sp, win->_notimeout EVENTLIST_2nd(evl)); 566 if (ch == KEY_MOUSE) { 567 ++runcount; 568 if (sp->_mouse_inline(sp)) 569 break; 570 } 571 if (sp->_maxclick < 0) 572 break; 573 } while 574 (ch == KEY_MOUSE 575 && (((rc = check_mouse_activity(sp, sp->_maxclick 576 EVENTLIST_2nd(evl))) != 0 577 && !(rc & TW_EVENT)) 578 || !sp->_mouse_parse(sp, runcount))); 579 #ifdef NCURSES_WGETCH_EVENTS 580 if ((rc & TW_EVENT) && !(ch == KEY_EVENT)) { 581 safe_ungetch(sp, ch); 582 ch = KEY_EVENT; 583 } 584 #endif 585 if (runcount > 0 && ch != KEY_MOUSE) { 586 #ifdef NCURSES_WGETCH_EVENTS 587 /* mouse event sequence ended by an event, report event */ 588 if (ch == KEY_EVENT) { 589 safe_ungetch(sp, KEY_MOUSE); /* FIXME This interrupts a gesture... */ 590 } else 591 #endif 592 { 593 /* mouse event sequence ended by keystroke, store keystroke */ 594 safe_ungetch(sp, ch); 595 ch = KEY_MOUSE; 596 } 597 } 598 } else { 599 if (head == -1) 600 fifo_push(sp EVENTLIST_2nd(evl)); 601 ch = fifo_pull(sp); 602 } 603 604 if (ch == ERR) { 605 check_sigwinch: 606 #if USE_SIZECHANGE 607 if (_nc_handle_sigwinch(sp)) { 608 _nc_update_screensize(sp); 609 /* resizeterm can push KEY_RESIZE */ 610 if (cooked_key_in_fifo()) { 611 *result = fifo_pull(sp); 612 /* 613 * Get the ERR from queue -- it is from WINCH, 614 * so we should take it out, the "error" is handled. 615 */ 616 if (fifo_peek(sp) == -1) 617 fifo_pull(sp); 618 returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK); 619 } 620 } 621 #endif 622 returnCode(ERR); 623 } 624 625 /* 626 * If echo() is in effect, display the printable version of the 627 * key on the screen. Carriage return and backspace are treated 628 * specially by Solaris curses: 629 * 630 * If carriage return is defined as a function key in the 631 * terminfo, e.g., kent, then Solaris may return either ^J (or ^M 632 * if nonl() is set) or KEY_ENTER depending on the echo() mode. 633 * We echo before translating carriage return based on nonl(), 634 * since the visual result simply moves the cursor to column 0. 635 * 636 * Backspace is a different matter. Solaris curses does not 637 * translate it to KEY_BACKSPACE if kbs=^H. This does not depend 638 * on the stty modes, but appears to be a hardcoded special case. 639 * This is a difference from ncurses, which uses the terminfo entry. 640 * However, we provide the same visual result as Solaris, moving the 641 * cursor to the left. 642 */ 643 if (IsEcho(sp) && !IS_PAD(win)) { 644 chtype backup = (chtype) ((ch == KEY_BACKSPACE) ? '\b' : ch); 645 if (backup < KEY_MIN) 646 wechochar(win, backup); 647 } 648 649 /* 650 * Simulate ICRNL mode 651 */ 652 if ((ch == '\r') && IsNl(sp)) 653 ch = '\n'; 654 655 /* Strip 8th-bit if so desired. We do this only for characters that 656 * are in the range 128-255, to provide compatibility with terminals 657 * that display only 7-bit characters. Note that 'ch' may be a 658 * function key at this point, so we mustn't strip _those_. 659 */ 660 if (!use_meta) 661 if ((ch < KEY_MIN) && (ch & 0x80)) 662 ch &= 0x7f; 663 664 T(("wgetch returning : %s", _nc_tracechar(sp, ch))); 665 666 *result = ch; 667 returnCode(ch >= KEY_MIN ? KEY_CODE_YES : OK); 668 } 669 670 #ifdef NCURSES_WGETCH_EVENTS 671 NCURSES_EXPORT(int) 672 wgetch_events(WINDOW *win, _nc_eventlist * evl) 673 { 674 int code; 675 int value; 676 677 T((T_CALLED("wgetch_events(%p,%p)"), (void *) win, (void *) evl)); 678 code = _nc_wgetch(win, 679 &value, 680 _nc_use_meta(win) 681 EVENTLIST_2nd(evl)); 682 if (code != ERR) 683 code = value; 684 returnCode(code); 685 } 686 #endif 687 688 NCURSES_EXPORT(int) 689 wgetch(WINDOW *win) 690 { 691 int code; 692 int value; 693 694 T((T_CALLED("wgetch(%p)"), (void *) win)); 695 code = _nc_wgetch(win, 696 &value, 697 _nc_use_meta(win) 698 EVENTLIST_2nd((_nc_eventlist *) 0)); 699 if (code != ERR) 700 code = value; 701 returnCode(code); 702 } 703 704 /* 705 ** int 706 ** kgetch() 707 ** 708 ** Get an input character, but take care of keypad sequences, returning 709 ** an appropriate code when one matches the input. After each character 710 ** is received, set an alarm call based on ESCDELAY. If no more of the 711 ** sequence is received by the time the alarm goes off, pass through 712 ** the sequence gotten so far. 713 ** 714 ** This function must be called when there are no cooked keys in queue. 715 ** (that is head==-1 || peek==head) 716 ** 717 */ 718 719 static int 720 kgetch(SCREEN *sp, bool forever EVENTLIST_2nd(_nc_eventlist * evl)) 721 { 722 TRIES *ptr; 723 int ch = 0; 724 int timeleft = forever ? 9999999 : GetEscdelay(sp); 725 726 TR(TRACE_IEVENT, ("kgetch() called")); 727 728 ptr = sp->_keytry; 729 730 for (;;) { 731 if (cooked_key_in_fifo() && sp->_fifo[head] >= KEY_MIN) { 732 break; 733 } else if (!raw_key_in_fifo()) { 734 ch = fifo_push(sp EVENTLIST_2nd(evl)); 735 if (ch == ERR) { 736 peek = head; /* the keys stay uninterpreted */ 737 return ERR; 738 } 739 #ifdef NCURSES_WGETCH_EVENTS 740 else if (ch == KEY_EVENT) { 741 peek = head; /* the keys stay uninterpreted */ 742 return fifo_pull(sp); /* Remove KEY_EVENT from the queue */ 743 } 744 #endif 745 } 746 747 ch = fifo_peek(sp); 748 if (ch >= KEY_MIN) { 749 /* If not first in queue, somebody put this key there on purpose in 750 * emergency. Consider it higher priority than the unfinished 751 * keysequence we are parsing. 752 */ 753 peek = head; 754 /* assume the key is the last in fifo */ 755 t_dec(); /* remove the key */ 756 return ch; 757 } 758 759 TR(TRACE_IEVENT, ("ch: %s", _nc_tracechar(sp, (unsigned char) ch))); 760 while ((ptr != NULL) && (ptr->ch != (unsigned char) ch)) 761 ptr = ptr->sibling; 762 763 if (ptr == NULL) { 764 TR(TRACE_IEVENT, ("ptr is null")); 765 break; 766 } 767 TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d", 768 (void *) ptr, ptr->ch, ptr->value)); 769 770 if (ptr->value != 0) { /* sequence terminated */ 771 TR(TRACE_IEVENT, ("end of sequence")); 772 if (peek == tail) { 773 fifo_clear(sp); 774 } else { 775 head = peek; 776 } 777 return (ptr->value); 778 } 779 780 ptr = ptr->child; 781 782 if (!raw_key_in_fifo()) { 783 int rc; 784 785 TR(TRACE_IEVENT, ("waiting for rest of sequence")); 786 rc = check_mouse_activity(sp, timeleft EVENTLIST_2nd(evl)); 787 #ifdef NCURSES_WGETCH_EVENTS 788 if (rc & TW_EVENT) { 789 TR(TRACE_IEVENT, ("interrupted by a user event")); 790 /* FIXME Should have preserved remainder timeleft for reuse... */ 791 peek = head; /* Restart interpreting later */ 792 return KEY_EVENT; 793 } 794 #endif 795 if (!rc) { 796 TR(TRACE_IEVENT, ("ran out of time")); 797 break; 798 } 799 } 800 } 801 ch = fifo_pull(sp); 802 peek = head; 803 return ch; 804 } 805