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