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