]> ncurses.scripts.mit.edu Git - ncurses.git/blob - ncurses/base/lib_getch.c
ncurses 5.6 - patch 20070707
[ncurses.git] / ncurses / base / lib_getch.c
1 /****************************************************************************
2  * Copyright (c) 1998-2006,2007 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28
29 /****************************************************************************
30  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32  *     and: Thomas E. Dickey                        1996-on                 *
33  ****************************************************************************/
34
35 /*
36 **      lib_getch.c
37 **
38 **      The routine getch().
39 **
40 */
41
42 #include <curses.priv.h>
43
44 MODULE_ID("$Id: lib_getch.c,v 1.79 2007/04/19 20:57:49 tom Exp $")
45
46 #include <fifo_defs.h>
47
48 #if USE_REENTRANT
49 NCURSES_EXPORT(int)
50 NCURSES_PUBLIC_VAR(ESCDELAY) (void)
51 {
52     return SP ? SP->_ESCDELAY : 1000;
53 }
54 #else
55 NCURSES_EXPORT_VAR(int)
56 ESCDELAY = 1000;                /* max interval betw. chars in funkeys, in millisecs */
57 #endif
58
59 #ifdef NCURSES_WGETCH_EVENTS
60 #define TWAIT_MASK 7
61 #else
62 #define TWAIT_MASK 3
63 #endif
64
65 /*
66  * Check for mouse activity, returning nonzero if we find any.
67  */
68 static int
69 check_mouse_activity(int delay EVENTLIST_2nd(_nc_eventlist * evl))
70 {
71     int rc;
72
73 #if USE_SYSMOUSE
74     if ((SP->_mouse_type == M_SYSMOUSE)
75         && (SP->_sysmouse_head < SP->_sysmouse_tail)) {
76         return 2;
77     }
78 #endif
79     rc = _nc_timed_wait(TWAIT_MASK, delay, (int *) 0 EVENTLIST_2nd(evl));
80 #if USE_SYSMOUSE
81     if ((SP->_mouse_type == M_SYSMOUSE)
82         && (SP->_sysmouse_head < SP->_sysmouse_tail)
83         && (rc == 0)
84         && (errno == EINTR)) {
85         rc |= 2;
86     }
87 #endif
88     return rc;
89 }
90
91 static NCURSES_INLINE int
92 fifo_peek(void)
93 {
94     int ch = SP->_fifo[peek];
95     TR(TRACE_IEVENT, ("peeking at %d", peek));
96
97     p_inc();
98     return ch;
99 }
100
101 static NCURSES_INLINE int
102 fifo_pull(void)
103 {
104     int ch;
105     ch = SP->_fifo[head];
106     TR(TRACE_IEVENT, ("pulling %s from %d", _tracechar(ch), head));
107
108     if (peek == head) {
109         h_inc();
110         peek = head;
111     } else
112         h_inc();
113
114 #ifdef TRACE
115     if (_nc_tracing & TRACE_IEVENT)
116         _nc_fifo_dump();
117 #endif
118     return ch;
119 }
120
121 static NCURSES_INLINE int
122 fifo_push(EVENTLIST_0th(_nc_eventlist * evl))
123 {
124     int n;
125     int ch = 0;
126     int mask = 0;
127
128     (void) mask;
129     if (tail == -1)
130         return ERR;
131
132 #ifdef HIDE_EINTR
133   again:
134     errno = 0;
135 #endif
136
137 #ifdef NCURSES_WGETCH_EVENTS
138     if (evl
139 #if USE_GPM_SUPPORT || USE_EMX_MOUSE || USE_SYSMOUSE
140         || (SP->_mouse_fd >= 0)
141 #endif
142         ) {
143         mask = check_mouse_activity(-1 EVENTLIST_2nd(evl));
144     } else
145         mask = 0;
146
147     if (mask & 4) {
148         T(("fifo_push: ungetch KEY_EVENT"));
149         ungetch(KEY_EVENT);
150         return KEY_EVENT;
151     }
152 #elif USE_GPM_SUPPORT || USE_EMX_MOUSE || USE_SYSMOUSE
153     if (SP->_mouse_fd >= 0) {
154         mask = check_mouse_activity(-1 EVENTLIST_2nd(evl));
155     }
156 #endif
157
158 #if USE_GPM_SUPPORT || USE_EMX_MOUSE
159     if ((SP->_mouse_fd >= 0) && (mask & 2)) {
160         SP->_mouse_event(SP);
161         ch = KEY_MOUSE;
162         n = 1;
163     } else
164 #endif
165 #if USE_SYSMOUSE
166         if ((SP->_mouse_type == M_SYSMOUSE)
167             && (SP->_sysmouse_head < SP->_sysmouse_tail)) {
168         SP->_mouse_event(SP);
169         ch = KEY_MOUSE;
170         n = 1;
171     } else if ((SP->_mouse_type == M_SYSMOUSE)
172                && (mask <= 0) && errno == EINTR) {
173         SP->_mouse_event(SP);
174         ch = KEY_MOUSE;
175         n = 1;
176     } else
177 #endif
178     {                           /* Can block... */
179         unsigned char c2 = 0;
180         n = read(SP->_ifd, &c2, 1);
181         ch = c2;
182     }
183
184 #ifdef HIDE_EINTR
185     /*
186      * Under System V curses with non-restarting signals, getch() returns
187      * with value ERR when a handled signal keeps it from completing.
188      * If signals restart system calls, OTOH, the signal is invisible
189      * except to its handler.
190      *
191      * We don't want this difference to show.  This piece of code
192      * tries to make it look like we always have restarting signals.
193      */
194     if (n <= 0 && errno == EINTR)
195         goto again;
196 #endif
197
198     if ((n == -1) || (n == 0)) {
199         TR(TRACE_IEVENT, ("read(%d,&ch,1)=%d, errno=%d", SP->_ifd, n, errno));
200         ch = ERR;
201     }
202     TR(TRACE_IEVENT, ("read %d characters", n));
203
204     SP->_fifo[tail] = ch;
205     SP->_fifohold = 0;
206     if (head == -1)
207         head = peek = tail;
208     t_inc();
209     TR(TRACE_IEVENT, ("pushed %s at %d", _tracechar(ch), tail));
210 #ifdef TRACE
211     if (_nc_tracing & TRACE_IEVENT)
212         _nc_fifo_dump();
213 #endif
214     return ch;
215 }
216
217 static NCURSES_INLINE void
218 fifo_clear(void)
219 {
220     memset(SP->_fifo, 0, sizeof(SP->_fifo));
221     head = -1;
222     tail = peek = 0;
223 }
224
225 static int kgetch(EVENTLIST_0th(_nc_eventlist * evl));
226
227 #define wgetch_should_refresh(win) (\
228         (is_wintouched(win) || (win->_flags & _HASMOVED)) \
229         && !(win->_flags & _ISPAD))
230
231 NCURSES_EXPORT(int)
232 _nc_wgetch(WINDOW *win,
233            unsigned long *result,
234            int use_meta
235            EVENTLIST_2nd(_nc_eventlist * evl))
236 {
237     int ch;
238 #ifdef NCURSES_WGETCH_EVENTS
239     long event_delay = -1;
240 #endif
241
242     T((T_CALLED("_nc_wgetch(%p)"), win));
243
244     *result = 0;
245     if (win == 0 || SP == 0) {
246         returnCode(ERR);
247     }
248
249     if (cooked_key_in_fifo()) {
250         if (wgetch_should_refresh(win))
251             wrefresh(win);
252
253         *result = fifo_pull();
254         returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK);
255     }
256 #ifdef NCURSES_WGETCH_EVENTS
257     if (evl && (evl->count == 0))
258         evl = NULL;
259     event_delay = _nc_eventlist_timeout(evl);
260 #endif
261
262     /*
263      * Handle cooked mode.  Grab a string from the screen,
264      * stuff its contents in the FIFO queue, and pop off
265      * the first character to return it.
266      */
267     if (head == -1 &&
268         !SP->_notty &&
269         !SP->_raw &&
270         !SP->_cbreak &&
271         !SP->_called_wgetch) {
272         char buf[MAXCOLUMNS], *sp;
273         int rc;
274
275         TR(TRACE_IEVENT, ("filling queue in cooked mode"));
276
277         SP->_called_wgetch = TRUE;
278         rc = wgetnstr(win, buf, MAXCOLUMNS);
279         SP->_called_wgetch = FALSE;
280
281         /* ungetch in reverse order */
282 #ifdef NCURSES_WGETCH_EVENTS
283         if (rc != KEY_EVENT)
284 #endif
285             ungetch('\n');
286         for (sp = buf + strlen(buf); sp > buf; sp--)
287             ungetch(sp[-1]);
288
289 #ifdef NCURSES_WGETCH_EVENTS
290         /* Return it first */
291         if (rc == KEY_EVENT) {
292             *result = rc;
293         } else
294 #endif
295             *result = fifo_pull();
296         returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK);
297     }
298
299     if (win->_use_keypad != SP->_keypad_on)
300         _nc_keypad(win->_use_keypad);
301
302     if (wgetch_should_refresh(win))
303         wrefresh(win);
304
305     if (!win->_notimeout && (win->_delay >= 0 || SP->_cbreak > 1)) {
306         if (head == -1) {       /* fifo is empty */
307             int delay;
308             int rc;
309
310             TR(TRACE_IEVENT, ("timed delay in wgetch()"));
311             if (SP->_cbreak > 1)
312                 delay = (SP->_cbreak - 1) * 100;
313             else
314                 delay = win->_delay;
315
316 #ifdef NCURSES_WGETCH_EVENTS
317             if (event_delay >= 0 && delay > event_delay)
318                 delay = event_delay;
319 #endif
320
321             TR(TRACE_IEVENT, ("delay is %d milliseconds", delay));
322
323             rc = check_mouse_activity(delay EVENTLIST_2nd(evl));
324
325 #ifdef NCURSES_WGETCH_EVENTS
326             if (rc & 4) {
327                 *result = KEY_EVENT;
328                 returnCode(KEY_CODE_YES);
329             }
330 #endif
331             if (!rc)
332                 returnCode(ERR);
333         }
334         /* else go on to read data available */
335     }
336
337     if (win->_use_keypad) {
338         /*
339          * This is tricky.  We only want to get special-key
340          * events one at a time.  But we want to accumulate
341          * mouse events until either (a) the mouse logic tells
342          * us it's picked up a complete gesture, or (b)
343          * there's a detectable time lapse after one.
344          *
345          * Note: if the mouse code starts failing to compose
346          * press/release events into clicks, you should probably
347          * increase the wait with mouseinterval().
348          */
349         int runcount = 0;
350         int rc;
351
352         do {
353             ch = kgetch(EVENTLIST_1st(evl));
354             if (ch == KEY_MOUSE) {
355                 ++runcount;
356                 if (SP->_mouse_inline(SP))
357                     break;
358             }
359             if (SP->_maxclick < 0)
360                 break;
361         } while
362             (ch == KEY_MOUSE
363              && (((rc = check_mouse_activity(SP->_maxclick
364                                              EVENTLIST_2nd(evl))) != 0
365                   && !(rc & 4))
366                  || !SP->_mouse_parse(runcount)));
367 #ifdef NCURSES_WGETCH_EVENTS
368         if ((rc & 4) && !ch == KEY_EVENT) {
369             ungetch(ch);
370             ch = KEY_EVENT;
371         }
372 #endif
373         if (runcount > 0 && ch != KEY_MOUSE) {
374 #ifdef NCURSES_WGETCH_EVENTS
375             /* mouse event sequence ended by an event, report event */
376             if (ch == KEY_EVENT) {
377                 ungetch(KEY_MOUSE);     /* FIXME This interrupts a gesture... */
378             } else
379 #endif
380             {
381                 /* mouse event sequence ended by keystroke, store keystroke */
382                 ungetch(ch);
383                 ch = KEY_MOUSE;
384             }
385         }
386     } else {
387         if (head == -1)
388             fifo_push(EVENTLIST_1st(evl));
389         ch = fifo_pull();
390     }
391
392     if (ch == ERR) {
393 #if USE_SIZECHANGE
394         if (_nc_handle_sigwinch(FALSE)) {
395             _nc_update_screensize();
396             /* resizeterm can push KEY_RESIZE */
397             if (cooked_key_in_fifo()) {
398                 *result = fifo_pull();
399                 returnCode(*result >= KEY_MIN ? KEY_CODE_YES : OK);
400             }
401         }
402 #endif
403         returnCode(ERR);
404     }
405
406     /*
407      * If echo() is in effect, display the printable version of the
408      * key on the screen.  Carriage return and backspace are treated
409      * specially by Solaris curses:
410      *
411      * If carriage return is defined as a function key in the
412      * terminfo, e.g., kent, then Solaris may return either ^J (or ^M
413      * if nonl() is set) or KEY_ENTER depending on the echo() mode. 
414      * We echo before translating carriage return based on nonl(),
415      * since the visual result simply moves the cursor to column 0.
416      *
417      * Backspace is a different matter.  Solaris curses does not
418      * translate it to KEY_BACKSPACE if kbs=^H.  This does not depend
419      * on the stty modes, but appears to be a hardcoded special case.
420      * This is a difference from ncurses, which uses the terminfo entry.
421      * However, we provide the same visual result as Solaris, moving the
422      * cursor to the left.
423      */
424     if (SP->_echo && !(win->_flags & _ISPAD)) {
425         chtype backup = (ch == KEY_BACKSPACE) ? '\b' : ch;
426         if (backup < KEY_MIN)
427             wechochar(win, backup);
428     }
429
430     /*
431      * Simulate ICRNL mode
432      */
433     if ((ch == '\r') && SP->_nl)
434         ch = '\n';
435
436     /* Strip 8th-bit if so desired.  We do this only for characters that
437      * are in the range 128-255, to provide compatibility with terminals
438      * that display only 7-bit characters.  Note that 'ch' may be a
439      * function key at this point, so we mustn't strip _those_.
440      */
441     if (!use_meta)
442         if ((ch < KEY_MIN) && (ch & 0x80))
443             ch &= 0x7f;
444
445     T(("wgetch returning : %s", _tracechar(ch)));
446
447     *result = ch;
448     returnCode(ch >= KEY_MIN ? KEY_CODE_YES : OK);
449 }
450
451 #ifdef NCURSES_WGETCH_EVENTS
452 NCURSES_EXPORT(int)
453 wgetch_events(WINDOW *win, _nc_eventlist * evl)
454 {
455     int code;
456     unsigned long value;
457
458     T((T_CALLED("wgetch_events(%p,%p)"), win, evl));
459     code = _nc_wgetch(win,
460                       &value,
461                       SP->_use_meta
462                       EVENTLIST_2nd(evl));
463     if (code != ERR)
464         code = value;
465     returnCode(code);
466 }
467 #endif
468
469 NCURSES_EXPORT(int)
470 wgetch(WINDOW *win)
471 {
472     int code;
473     unsigned long value;
474
475     T((T_CALLED("wgetch(%p)"), win));
476     code = _nc_wgetch(win,
477                       &value,
478                       (SP ? SP->_use_meta : 0)
479                       EVENTLIST_2nd((_nc_eventlist *) 0));
480     if (code != ERR)
481         code = value;
482     returnCode(code);
483 }
484
485 /*
486 **      int
487 **      kgetch()
488 **
489 **      Get an input character, but take care of keypad sequences, returning
490 **      an appropriate code when one matches the input.  After each character
491 **      is received, set an alarm call based on ESCDELAY.  If no more of the
492 **      sequence is received by the time the alarm goes off, pass through
493 **      the sequence gotten so far.
494 **
495 **      This function must be called when there are no cooked keys in queue.
496 **      (that is head==-1 || peek==head)
497 **
498 */
499
500 static int
501 kgetch(EVENTLIST_0th(_nc_eventlist * evl))
502 {
503     TRIES *ptr;
504     int ch = 0;
505     int timeleft = ESCDELAY;
506
507     TR(TRACE_IEVENT, ("kgetch() called"));
508
509     ptr = SP->_keytry;
510
511     for (;;) {
512         if (cooked_key_in_fifo() && SP->_fifo[head] >= KEY_MIN) {
513             break;
514         } else if (!raw_key_in_fifo()) {
515             ch = fifo_push(EVENTLIST_1st(evl));
516             if (ch == ERR) {
517                 peek = head;    /* the keys stay uninterpreted */
518                 return ERR;
519             }
520 #ifdef NCURSES_WGETCH_EVENTS
521             else if (ch == KEY_EVENT) {
522                 peek = head;    /* the keys stay uninterpreted */
523                 return fifo_pull();     /* Remove KEY_EVENT from the queue */
524             }
525 #endif
526         }
527
528         ch = fifo_peek();
529         if (ch >= KEY_MIN) {
530             /* If not first in queue, somebody put this key there on purpose in
531              * emergency.  Consider it higher priority than the unfinished
532              * keysequence we are parsing.
533              */
534             peek = head;
535             /* assume the key is the last in fifo */
536             t_dec();            /* remove the key */
537             return ch;
538         }
539
540         TR(TRACE_IEVENT, ("ch: %s", _tracechar((unsigned char) ch)));
541         while ((ptr != NULL) && (ptr->ch != (unsigned char) ch))
542             ptr = ptr->sibling;
543
544         if (ptr == NULL) {
545             TR(TRACE_IEVENT, ("ptr is null"));
546             break;
547         }
548         TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d",
549                           ptr, ptr->ch, ptr->value));
550
551         if (ptr->value != 0) {  /* sequence terminated */
552             TR(TRACE_IEVENT, ("end of sequence"));
553             if (peek == tail)
554                 fifo_clear();
555             else
556                 head = peek;
557             return (ptr->value);
558         }
559
560         ptr = ptr->child;
561
562         if (!raw_key_in_fifo()) {
563             int rc;
564
565             TR(TRACE_IEVENT, ("waiting for rest of sequence"));
566             rc = check_mouse_activity(timeleft EVENTLIST_2nd(evl));
567 #ifdef NCURSES_WGETCH_EVENTS
568             if (rc & 4) {
569                 TR(TRACE_IEVENT, ("interrupted by a user event"));
570                 /* FIXME Should have preserved remainder timeleft for reuse... */
571                 peek = head;    /* Restart interpreting later */
572                 return KEY_EVENT;
573             }
574 #endif
575             if (!rc) {
576                 TR(TRACE_IEVENT, ("ran out of time"));
577                 break;
578             }
579         }
580     }
581     ch = fifo_pull();
582     peek = head;
583     return ch;
584 }