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