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