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