ncurses 5.2
[ncurses.git] / ncurses / base / lib_getch.c
1 /****************************************************************************
2  * Copyright (c) 1998,1999,2000 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  ****************************************************************************/
33
34 /*
35 **      lib_getch.c
36 **
37 **      The routine getch().
38 **
39 */
40
41 #include <curses.priv.h>
42
43 MODULE_ID("$Id: lib_getch.c,v 1.50 2000/10/09 23:53:57 Ilya.Zakharevich Exp $")
44
45 #include <fifo_defs.h>
46
47 int ESCDELAY = 1000;            /* max interval betw. chars in funkeys, in millisecs */
48
49 static inline int
50 fifo_peek(void)
51 {
52     int ch = SP->_fifo[peek];
53     TR(TRACE_IEVENT, ("peeking at %d", peek));
54
55     p_inc();
56     return ch;
57 }
58
59 static inline int
60 fifo_pull(void)
61 {
62     int ch;
63     ch = SP->_fifo[head];
64     TR(TRACE_IEVENT, ("pulling %d from %d", ch, head));
65
66     if (peek == head) {
67         h_inc();
68         peek = head;
69     } else
70         h_inc();
71
72 #ifdef TRACE
73     if (_nc_tracing & TRACE_IEVENT)
74         _nc_fifo_dump();
75 #endif
76     return ch;
77 }
78
79 static inline int
80 fifo_push(void)
81 {
82     int n;
83     unsigned int ch;
84
85     if (tail == -1)
86         return ERR;
87
88 #ifdef HIDE_EINTR
89   again:
90     errno = 0;
91 #endif
92
93 #if USE_GPM_SUPPORT || defined(USE_EMX_MOUSE)
94     if ((SP->_mouse_fd >= 0)
95         && (_nc_timed_wait(3, -1, (int *) 0) & 2)) {
96         SP->_mouse_event(SP);
97         ch = KEY_MOUSE;
98         n = 1;
99     } else
100 #endif
101     {
102         unsigned char c2 = 0;
103         n = read(SP->_ifd, &c2, 1);
104         ch = c2 & 0xff;
105     }
106
107 #ifdef HIDE_EINTR
108     /*
109      * Under System V curses with non-restarting signals, getch() returns
110      * with value ERR when a handled signal keeps it from completing.
111      * If signals restart system calls, OTOH, the signal is invisible
112      * except to its handler.
113      *
114      * We don't want this difference to show.  This piece of code
115      * tries to make it look like we always have restarting signals.
116      */
117     if (n <= 0 && errno == EINTR)
118         goto again;
119 #endif
120
121     if ((n == -1) || (n == 0)) {
122         TR(TRACE_IEVENT, ("read(%d,&ch,1)=%d, errno=%d", SP->_ifd, n, errno));
123         ch = ERR;
124     }
125     TR(TRACE_IEVENT, ("read %d characters", n));
126
127     SP->_fifo[tail] = ch;
128     SP->_fifohold = 0;
129     if (head == -1)
130         head = peek = tail;
131     t_inc();
132     TR(TRACE_IEVENT, ("pushed %#x at %d", ch, tail));
133 #ifdef TRACE
134     if (_nc_tracing & TRACE_IEVENT)
135         _nc_fifo_dump();
136 #endif
137     return ch;
138 }
139
140 static inline void
141 fifo_clear(void)
142 {
143     int i;
144     for (i = 0; i < FIFO_SIZE; i++)
145         SP->_fifo[i] = 0;
146     head = -1;
147     tail = peek = 0;
148 }
149
150 static int kgetch(WINDOW *);
151
152 #define wgetch_should_refresh(win) (\
153         (is_wintouched(win) || (win->_flags & _HASMOVED)) \
154         && !(win->_flags & _ISPAD))
155
156 int
157 wgetch(WINDOW *win)
158 {
159     int ch;
160
161     T((T_CALLED("wgetch(%p)"), win));
162
163     if (!win)
164         returnCode(ERR);
165
166     if (cooked_key_in_fifo()) {
167         if (wgetch_should_refresh(win))
168             wrefresh(win);
169
170         ch = fifo_pull();
171         T(("wgetch returning (pre-cooked): %#x = %s", ch, _trace_key(ch)));
172         returnCode(ch);
173     }
174
175     /*
176      * Handle cooked mode.  Grab a string from the screen,
177      * stuff its contents in the FIFO queue, and pop off
178      * the first character to return it.
179      */
180     if (head == -1 && !SP->_raw && !SP->_cbreak) {
181         char buf[MAXCOLUMNS], *sp;
182
183         TR(TRACE_IEVENT, ("filling queue in cooked mode"));
184
185         wgetnstr(win, buf, MAXCOLUMNS);
186
187         /* ungetch in reverse order */
188         ungetch('\n');
189         for (sp = buf + strlen(buf); sp > buf; sp--)
190             ungetch(sp[-1]);
191
192         returnCode(fifo_pull());
193     }
194
195     if (wgetch_should_refresh(win))
196         wrefresh(win);
197
198     if (!win->_notimeout && (win->_delay >= 0 || SP->_cbreak > 1)) {
199         int delay;
200
201         TR(TRACE_IEVENT, ("timed delay in wgetch()"));
202         if (SP->_cbreak > 1)
203             delay = (SP->_cbreak - 1) * 100;
204         else
205             delay = win->_delay;
206
207         TR(TRACE_IEVENT, ("delay is %d milliseconds", delay));
208
209         if (head == -1)         /* fifo is empty */
210             if (!_nc_timed_wait(3, delay, (int *) 0))
211                 returnCode(ERR);
212         /* else go on to read data available */
213     }
214
215     if (win->_use_keypad) {
216         /*
217          * This is tricky.  We only want to get special-key
218          * events one at a time.  But we want to accumulate
219          * mouse events until either (a) the mouse logic tells
220          * us it's picked up a complete gesture, or (b)
221          * there's a detectable time lapse after one.
222          *
223          * Note: if the mouse code starts failing to compose
224          * press/release events into clicks, you should probably
225          * increase the wait with mouseinterval().
226          */
227         int runcount = 0;
228
229         do {
230             ch = kgetch(win);
231             if (ch == KEY_MOUSE) {
232                 ++runcount;
233                 if (SP->_mouse_inline(SP))
234                     break;
235             }
236         } while
237             (ch == KEY_MOUSE
238             && (_nc_timed_wait(3, SP->_maxclick, (int *) 0)
239                 || !SP->_mouse_parse(runcount)));
240         if (runcount > 0 && ch != KEY_MOUSE) {
241             /* mouse event sequence ended by keystroke, push it */
242             ungetch(ch);
243             ch = KEY_MOUSE;
244         }
245     } else {
246         if (head == -1)
247             fifo_push();
248         ch = fifo_pull();
249     }
250
251     if (ch == ERR) {
252 #if USE_SIZECHANGE
253         if (SP->_sig_winch) {
254             _nc_update_screensize();
255             /* resizeterm can push KEY_RESIZE */
256             if (cooked_key_in_fifo()) {
257                 ch = fifo_pull();
258                 T(("wgetch returning (pre-cooked): %#x = %s", ch, _trace_key(ch)));
259                 returnCode(ch);
260             }
261         }
262 #endif
263         T(("wgetch returning ERR"));
264         returnCode(ERR);
265     }
266
267     /*
268      * If echo() is in effect, display the printable version of the
269      * key on the screen.  Carriage return and backspace are treated
270      * specially by Solaris curses:
271      *
272      * If carriage return is defined as a function key in the
273      * terminfo, e.g., kent, then Solaris may return either ^J (or ^M
274      * if nonl() is set) or KEY_ENTER depending on the echo() mode. 
275      * We echo before translating carriage return based on nonl(),
276      * since the visual result simply moves the cursor to column 0.
277      *
278      * Backspace is a different matter.  Solaris curses does not
279      * translate it to KEY_BACKSPACE if kbs=^H.  This does not depend
280      * on the stty modes, but appears to be a hardcoded special case.
281      * This is a difference from ncurses, which uses the terminfo entry.
282      * However, we provide the same visual result as Solaris, moving the
283      * cursor to the left.
284      */
285     if (SP->_echo && !(win->_flags & _ISPAD)) {
286         chtype backup = (ch == KEY_BACKSPACE) ? '\b' : ch;
287         if (backup < KEY_MIN)
288             wechochar(win, backup);
289     }
290
291     /*
292      * Simulate ICRNL mode
293      */
294     if ((ch == '\r') && SP->_nl)
295         ch = '\n';
296
297     /* Strip 8th-bit if so desired.  We do this only for characters that
298      * are in the range 128-255, to provide compatibility with terminals
299      * that display only 7-bit characters.  Note that 'ch' may be a
300      * function key at this point, so we mustn't strip _those_.
301      */
302     if ((ch < KEY_MIN) && (ch & 0x80))
303         if (!SP->_use_meta)
304             ch &= 0x7f;
305
306     T(("wgetch returning : %#x = %s", ch, _trace_key(ch)));
307
308     returnCode(ch);
309 }
310
311 /*
312 **      int
313 **      kgetch()
314 **
315 **      Get an input character, but take care of keypad sequences, returning
316 **      an appropriate code when one matches the input.  After each character
317 **      is received, set an alarm call based on ESCDELAY.  If no more of the
318 **      sequence is received by the time the alarm goes off, pass through
319 **      the sequence gotten so far.
320 **
321 **      This function must be called when there is no cooked keys in queue.
322 **      (that is head==-1 || peek==head)
323 **
324 */
325
326 static int
327 kgetch(WINDOW *win GCC_UNUSED)
328 {
329     struct tries *ptr;
330     int ch = 0;
331     int timeleft = ESCDELAY;
332
333     TR(TRACE_IEVENT, ("kgetch(%p) called", win));
334
335     ptr = SP->_keytry;
336
337     for (;;) {
338         if (!raw_key_in_fifo()) {
339             if (fifo_push() == ERR) {
340                 peek = head;    /* the keys stay uninterpreted */
341                 return ERR;
342             }
343         }
344         ch = fifo_peek();
345         if (ch >= KEY_MIN) {
346             peek = head;
347             /* assume the key is the last in fifo */
348             t_dec();            /* remove the key */
349             return ch;
350         }
351
352         TR(TRACE_IEVENT, ("ch: %s", _trace_key((unsigned char) ch)));
353         while ((ptr != NULL) && (ptr->ch != (unsigned char) ch))
354             ptr = ptr->sibling;
355 #ifdef TRACE
356         if (ptr == NULL) {
357             TR(TRACE_IEVENT, ("ptr is null"));
358         } else
359             TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d",
360                     ptr, ptr->ch, ptr->value));
361 #endif /* TRACE */
362
363         if (ptr == NULL)
364             break;
365
366         if (ptr->value != 0) {  /* sequence terminated */
367             TR(TRACE_IEVENT, ("end of sequence"));
368             if (peek == tail)
369                 fifo_clear();
370             else
371                 head = peek;
372             return (ptr->value);
373         }
374
375         ptr = ptr->child;
376
377         if (!raw_key_in_fifo()) {
378             TR(TRACE_IEVENT, ("waiting for rest of sequence"));
379             if (!_nc_timed_wait(3, timeleft, &timeleft)) {
380                 TR(TRACE_IEVENT, ("ran out of time"));
381                 break;
382             }
383         }
384     }
385     ch = fifo_pull();
386     peek = head;
387     return ch;
388 }