ncurses 4.1
[ncurses.git] / ncurses / lib_getch.c
1
2 /***************************************************************************
3 *                            COPYRIGHT NOTICE                              *
4 ****************************************************************************
5 *                ncurses is copyright (C) 1992-1995                        *
6 *                          Zeyd M. Ben-Halim                               *
7 *                          zmbenhal@netcom.com                             *
8 *                          Eric S. Raymond                                 *
9 *                          esr@snark.thyrsus.com                           *
10 *                                                                          *
11 *        Permission is hereby granted to reproduce and distribute ncurses  *
12 *        by any means and for any fee, whether alone or as part of a       *
13 *        larger distribution, in source or in binary form, PROVIDED        *
14 *        this notice is included with any such distribution, and is not    *
15 *        removed from any of its header files. Mention of ncurses in any   *
16 *        applications linked with it is highly appreciated.                *
17 *                                                                          *
18 *        ncurses comes AS IS with no warranty, implied or expressed.       *
19 *                                                                          *
20 ***************************************************************************/
21
22 /*
23 **      lib_getch.c
24 **
25 **      The routine getch().
26 **
27 */
28
29 #include <curses.priv.h>
30
31 MODULE_ID("$Id: lib_getch.c,v 1.24 1997/02/15 21:12:16 tom Exp $")
32
33 #define head    SP->_fifohead
34 #define tail    SP->_fifotail
35 #define peek    SP->_fifopeek
36
37 #define h_inc() { head == FIFO_SIZE-1 ? head = 0 : head++; if (head == tail) head = -1, tail = 0;}
38 #define h_dec() { head == 0 ?  head = FIFO_SIZE-1 : head--; if (head == tail) tail = -1;}
39 #define t_inc() { tail == FIFO_SIZE-1 ? tail = 0 : tail++; if (tail == head) tail = -1;}
40 #define p_inc() { peek == FIFO_SIZE-1 ? peek = 0 : peek++;}
41
42 int ESCDELAY = 1000;    /* max interval betw. chars in funkeys, in millisecs */
43
44 static int fifo_peek(void)
45 {
46         T(("peeking at %d", peek+1));
47         return SP->_fifo[++peek];
48 }
49
50 #ifdef TRACE
51 static inline void fifo_dump(void)
52 {
53 int i;
54         T(("head = %d, tail = %d, peek = %d", head, tail, peek));
55         for (i = 0; i < 10; i++)
56                 T(("char %d = %s", i, _trace_key(SP->_fifo[i])));
57 }
58 #endif /* TRACE */
59
60 static inline int fifo_pull(void)
61 {
62 int ch;
63         ch = SP->_fifo[head];
64         T(("pulling %d from %d", ch, head));
65
66         h_inc();
67 #ifdef TRACE
68         if (_nc_tracing & TRACE_IEVENT) fifo_dump();
69 #endif
70         return ch;
71 }
72
73 int ungetch(int ch)
74 {
75         if (tail == -1)
76                 return ERR;
77         if (head == -1) {
78                 head = 0;
79                 t_inc()
80         } else
81                 h_dec();
82
83         SP->_fifo[head] = ch;
84         T(("ungetch ok"));
85 #ifdef TRACE
86         if (_nc_tracing & TRACE_IEVENT) fifo_dump();
87 #endif
88         return OK;
89 }
90
91 static inline int fifo_push(void)
92 {
93 int n;
94 unsigned int ch;
95
96         if (tail == -1) return ERR;
97         /* FALLTHRU */
98 again:
99         errno = 0;
100 #if USE_GPM_SUPPORT     
101         if ((_nc_mouse_fd() >= 0) 
102          && (_nc_timed_wait(3, -1, (int *)0) & 2))
103         {
104                 _nc_mouse_event(SP);
105                 ch = KEY_MOUSE;
106                 n = 1;
107         } else
108 #endif
109         {
110                 unsigned char c2;
111                 n = read(SP->_ifd, &c2, 1);
112                 ch = c2;
113         }
114
115         /*
116          * Under System V curses with non-restarting signals, getch() returns
117          * with value ERR when a handled signal keeps it from completing.  
118          * If signals restart system calls, OTOH, the signal is invisible
119          * except to its handler.
120          * 
121          * We don't want this difference to show.  This piece of code
122          * tries to make it look like we always have restarting signals.
123          */
124         if (n <= 0 && errno == EINTR)
125                 goto again;
126
127         if ((n == -1) || (n == 0))
128         {
129             T(("read(%d,&ch,1)=%d", SP->_ifd, n));
130             return ERR;
131         }
132         T(("read %d characters", n));
133
134         SP->_fifo[tail] = ch;
135         if (head == -1) head = tail;
136         t_inc();
137         T(("pushed %#x at %d", ch, tail));
138 #ifdef TRACE
139         if (_nc_tracing & TRACE_IEVENT) fifo_dump();
140 #endif
141         return ch;
142 }
143
144 static inline void fifo_clear(void)
145 {
146 int i;
147         for (i = 0; i < FIFO_SIZE; i++)
148                 SP->_fifo[i] = 0;
149         head = -1; tail = peek = 0;
150 }
151
152 static int kgetch(WINDOW *);
153
154 void _nc_backspace(WINDOW *win)
155 {
156         if (win->_curx == 0)
157         {
158             beep();
159             return;
160         }
161
162         mvwaddstr(curscr, win->_begy + win->_cury + win->_yoffset,
163                   win->_begx + win->_curx, "\b \b");
164         waddstr(win, "\b \b");
165
166         /*
167          * This used to do the equivalent of _nc_outstr("\b \b"), which
168          * would fail on terminals with a non-backspace cursor_left
169          * character.
170          */
171         mvcur(win->_begy + win->_cury + win->_yoffset,
172               win->_begx + win->_curx,
173               win->_begy + win->_cury + win->_yoffset,
174               win->_begx + win->_curx - 1);
175         _nc_outstr(" ");
176         mvcur(win->_begy + win->_cury + win->_yoffset,
177               win->_begx + win->_curx,
178               win->_begy + win->_cury + win->_yoffset,
179               win->_begx + win->_curx - 1);
180         SP->_curscol--;
181 }
182
183 int
184 wgetch(WINDOW *win)
185 {
186 int     ch;
187
188         T((T_CALLED("wgetch(%p)"), win));
189
190         /*
191          * Handle cooked mode.  Grab a string from the screen,
192          * stuff its contents in the FIFO queue, and pop off
193          * the first character to return it.
194          */
195         if (head == -1 && !SP->_raw && !SP->_cbreak)
196         {
197                 char    buf[MAXCOLUMNS], *sp;
198
199                 T(("filling queue in cooked mode"));
200
201                 wgetnstr(win, buf, MAXCOLUMNS);
202
203                 for (sp = buf; *sp; sp++)
204                         ungetch(*sp);
205                 ungetch('\n');
206
207                 return(fifo_pull());
208         }
209
210         /* this should be eliminated */
211         if (!has_ic()
212          && !win->_scroll
213          &&  (SP->_echo)
214          &&  (win->_flags & _FULLWIN)
215          &&  win->_curx == win->_maxx
216          &&  win->_cury == win->_maxy)
217                 returnCode(ERR);
218
219         if ((is_wintouched(win) || (win->_flags & _HASMOVED)) && !(win->_flags & _ISPAD))
220                 wrefresh(win);
221
222         if (!win->_notimeout && (win->_delay >= 0 || SP->_cbreak > 1)) {
223         int delay;
224
225                 T(("timed delay in wgetch()"));
226                 if (SP->_cbreak > 1)
227                     delay = (SP->_cbreak-1) * 100;
228                 else
229                     delay = win->_delay;
230
231                 T(("delay is %d microseconds", delay));
232
233                 if (head == -1) /* fifo is empty */
234                         if (!_nc_timed_wait(3, delay, (int *)0))
235                                 returnCode(ERR);
236                 /* else go on to read data available */
237         }
238
239         if (win->_use_keypad) {
240                 /*
241                  * This is tricky.  We only want to get special-key
242                  * events one at a time.  But we want to accumulate
243                  * mouse events until either (a) the mouse logic tells
244                  * us it's picked up a complete gesture, or (b)
245                  * there's a detectable time lapse after one.
246                  *
247                  * Note: if the mouse code starts failing to compose
248                  * press/release events into clicks, you should probably
249                  * increase _nc_max_click_interval.
250                  */
251                 int runcount = 0;
252
253                 do {
254                         ch = kgetch(win);
255                         if (ch == KEY_MOUSE)
256                         {
257                                 ++runcount;
258                                 if (_nc_mouse_inline(SP))
259                                     break;
260                         }
261                 } while
262                     (ch == KEY_MOUSE
263                      && (_nc_timed_wait(3, _nc_max_click_interval, (int *)0)
264                          || !_nc_mouse_parse(runcount)));
265                 if (runcount > 0 && ch != KEY_MOUSE)
266                 {
267                     /* mouse event sequence ended by keystroke, push it */
268                     ungetch(ch);
269                     ch = KEY_MOUSE;
270                 }
271         } else {
272                 if (head == -1)
273                         fifo_push();
274                 ch = fifo_pull();
275         }
276
277         if (ch == ERR)
278         {
279             T(("wgetch returning ERR"));
280             returnCode(ERR);
281         }
282
283         /*
284          * Simulate ICRNL mode
285          */
286         if ((ch == '\r') && SP->_nl)
287                 ch = '\n';
288
289         /* Strip 8th-bit if so desired.  We do this only for characters that
290          * are in the range 128-255, to provide compatibility with terminals
291          * that display only 7-bit characters.  Note that 'ch' may be a
292          * function key at this point, so we mustn't strip _those_.
293          */
294         if ((ch < KEY_MIN) && (ch & 0x80))
295                 if (!SP->_use_meta)
296                         ch &= 0x7f;
297
298         if (!(win->_flags & _ISPAD) && SP->_echo) {
299             /* there must be a simpler way of doing this */
300             if (ch == erasechar() || ch == KEY_BACKSPACE || ch == KEY_LEFT)
301                 _nc_backspace(win);
302             else if (ch < KEY_MIN) {
303                 mvwaddch(curscr,
304                          win->_begy + win->_cury + win->_yoffset,
305                          win->_begx + win->_curx,
306                          ch);
307                 waddch(win, (chtype)ch);
308             }
309             else
310                 beep();
311         }
312
313         T(("wgetch returning : %#x = %s", ch, _trace_key(ch));)
314
315         returnCode(ch);
316 }
317
318
319 /*
320 **      int
321 **      kgetch()
322 **
323 **      Get an input character, but take care of keypad sequences, returning
324 **      an appropriate code when one matches the input.  After each character
325 **      is received, set an alarm call based on ESCDELAY.  If no more of the
326 **      sequence is received by the time the alarm goes off, pass through
327 **      the sequence gotten so far.
328 **
329 */
330
331 static int
332 kgetch(WINDOW *win GCC_UNUSED)
333 {
334 struct tries  *ptr;
335 int ch = 0;
336 int timeleft = ESCDELAY;
337
338         TR(TRACE_IEVENT, ("kgetch(%p) called", win));
339
340         ptr = SP->_keytry;
341
342         if (head == -1)  {
343                 if ((ch = fifo_push()) == ERR)
344                     return ERR;
345                 peek = 0;
346                 while (ptr != NULL) {
347                         TR(TRACE_IEVENT, ("ch: %s", _trace_key((unsigned char)ch)));
348                         while ((ptr != NULL) && (ptr->ch != (unsigned char)ch))
349                                 ptr = ptr->sibling;
350 #ifdef TRACE
351                         if (ptr == NULL)
352                                 {TR(TRACE_IEVENT, ("ptr is null"));}
353                         else
354                                 TR(TRACE_IEVENT, ("ptr=%p, ch=%d, value=%d",
355                                                 ptr, ptr->ch, ptr->value));
356 #endif /* TRACE */
357
358                         if (ptr != NULL)
359                                 if (ptr->value != 0) {  /* sequence terminated */
360                                         TR(TRACE_IEVENT, ("end of sequence"));
361                                         fifo_clear();
362                                         return(ptr->value);
363                                 } else {                /* go back for another character */
364                                         ptr = ptr->child;
365                                         TR(TRACE_IEVENT, ("going back for more"));
366                                 } else
367                                         break;
368
369                                 TR(TRACE_IEVENT, ("waiting for rest of sequence"));
370                                 if (!_nc_timed_wait(3, timeleft, &timeleft)) {
371                                         TR(TRACE_IEVENT, ("ran out of time"));
372                                         return(fifo_pull());
373                                 } else {
374                                         TR(TRACE_IEVENT, ("got more!"));
375                                         fifo_push();
376                                         ch = fifo_peek();
377                                 }
378                 }
379         }
380         return(fifo_pull());
381 }