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