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