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