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