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