ncurses 5.6 - patch 20070512
[ncurses.git] / ncurses / tty / lib_twait.c
1 /****************************************************************************
2  * Copyright (c) 1998-2006,2007 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  *     and: Thomas E. Dickey                        1996-on                 *
33  ****************************************************************************/
34
35 /*
36 **      lib_twait.c
37 **
38 **      The routine _nc_timed_wait().
39 **
40 **      (This file was originally written by Eric Raymond; however except for
41 **      comments, none of the original code remains - T.Dickey).
42 */
43
44 #include <curses.priv.h>
45
46 #ifdef __BEOS__
47 #undef false
48 #undef true
49 #include <OS.h>
50 #endif
51
52 #if USE_FUNC_POLL
53 # if HAVE_SYS_TIME_H
54 #  include <sys/time.h>
55 # endif
56 #elif HAVE_SELECT
57 # if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
58 #  include <sys/time.h>
59 # endif
60 # if HAVE_SYS_SELECT_H
61 #  include <sys/select.h>
62 # endif
63 #endif
64
65 MODULE_ID("$Id: lib_twait.c,v 1.53 2007/05/12 19:02:31 tom Exp $")
66
67 #if HAVE_GETTIMEOFDAY
68 # define PRECISE_GETTIME 1
69 # define TimeType struct timeval
70 #else
71 # define PRECISE_GETTIME 0
72 # define TimeType time_t
73 #endif
74
75 static long
76 _nc_gettime(TimeType * t0, bool first)
77 {
78     long res;
79
80 #if PRECISE_GETTIME
81     TimeType t1;
82     gettimeofday(&t1, (struct timezone *) 0);
83     if (first) {
84         *t0 = t1;
85         res = 0;
86     } else {
87         /* .tv_sec and .tv_usec are unsigned, be careful when subtracting */
88         if (t0->tv_usec > t1.tv_usec) {
89             t1.tv_usec += 1000000;      /* Convert 1s in 1e6 microsecs */
90             t1.tv_sec--;
91         }
92         res = (t1.tv_sec - t0->tv_sec) * 1000
93             + (t1.tv_usec - t0->tv_usec) / 1000;
94     }
95 #else
96     time_t t1 = time((time_t *) 0);
97     if (first) {
98         *t0 = t1;
99     }
100     res = (t1 - *t0) * 1000;
101 #endif
102     TR(TRACE_IEVENT, ("%s time: %ld msec", first ? "get" : "elapsed", res));
103     return res;
104 }
105
106 #ifdef NCURSES_WGETCH_EVENTS
107 NCURSES_EXPORT(int)
108 _nc_eventlist_timeout(_nc_eventlist * evl)
109 {
110     int event_delay = -1;
111     int n;
112
113     if (evl != 0) {
114
115         for (n = 0; n < evl->count; ++n) {
116             _nc_event *ev = evl->events[n];
117
118             if (ev->type == _NC_EVENT_TIMEOUT_MSEC) {
119                 event_delay = ev->data.timeout_msec;
120                 if (event_delay < 0)
121                     event_delay = INT_MAX;      /* FIXME Is this defined? */
122             }
123         }
124     }
125     return event_delay;
126 }
127 #endif /* NCURSES_WGETCH_EVENTS */
128
129 /*
130  * Wait a specified number of milliseconds, returning nonzero if the timer
131  * didn't expire before there is activity on the specified file descriptors.
132  * The file-descriptors are specified by the mode:
133  *      0 - none (absolute time)
134  *      1 - ncurses' normal input-descriptor
135  *      2 - mouse descriptor, if any
136  *      3 - either input or mouse.
137  *
138  * Experimental:  if NCURSES_WGETCH_EVENTS is defined, (mode & 4) determines
139  * whether to pay attention to evl argument.  If set, the smallest of
140  * millisecond and of timeout of evl is taken.
141  *
142  * We return a mask that corresponds to the mode (e.g., 2 for mouse activity).
143  *
144  * If the milliseconds given are -1, the wait blocks until activity on the
145  * descriptors.
146  */
147 NCURSES_EXPORT(int)
148 _nc_timed_wait(int mode,
149                int milliseconds,
150                int *timeleft
151                EVENTLIST_2nd(_nc_eventlist * evl))
152 {
153     int fd;
154     int count;
155     int result;
156     TimeType t0;
157
158 #ifdef NCURSES_WGETCH_EVENTS
159     int timeout_is_event = 0;
160     int n;
161 #endif
162
163 #if USE_FUNC_POLL
164 #define MIN_FDS 2
165     struct pollfd fd_list[MIN_FDS];
166     struct pollfd *fds = fd_list;
167 #elif defined(__BEOS__)
168 #elif HAVE_SELECT
169     fd_set set;
170 #endif
171
172     long starttime, returntime;
173
174     TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
175                       milliseconds, mode));
176
177 #ifdef NCURSES_WGETCH_EVENTS
178     if (mode & 4) {
179         int event_delay = _nc_eventlist_timeout(evl);
180
181         if (event_delay >= 0
182             && (milliseconds >= event_delay || milliseconds < 0)) {
183             milliseconds = event_delay;
184             timeout_is_event = 1;
185         }
186     }
187 #endif
188
189 #if PRECISE_GETTIME && HAVE_NANOSLEEP
190   retry:
191 #endif
192     starttime = _nc_gettime(&t0, TRUE);
193
194     count = 0;
195
196 #ifdef NCURSES_WGETCH_EVENTS
197     if ((mode & 4) && evl)
198         evl->result_flags = 0;
199 #endif
200
201 #if USE_FUNC_POLL
202     memset(fd_list, 0, sizeof(fd_list));
203
204 #ifdef NCURSES_WGETCH_EVENTS
205     if ((mode & 4) && evl)
206         fds = typeMalloc(struct pollfd, MIN_FDS + evl->count);
207 #endif
208
209     if (mode & 1) {
210         fds[count].fd = SP->_ifd;
211         fds[count].events = POLLIN;
212         count++;
213     }
214     if ((mode & 2)
215         && (fd = SP->_mouse_fd) >= 0) {
216         fds[count].fd = fd;
217         fds[count].events = POLLIN;
218         count++;
219     }
220 #ifdef NCURSES_WGETCH_EVENTS
221     if ((mode & 4) && evl) {
222         for (n = 0; n < evl->count; ++n) {
223             _nc_event *ev = evl->events[n];
224
225             if (ev->type == _NC_EVENT_FILE
226                 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) {
227                 fds[count].fd = ev->data.fev.fd;
228                 fds[count].events = POLLIN;
229                 count++;
230             }
231         }
232     }
233 #endif
234
235     result = poll(fds, (unsigned) count, milliseconds);
236
237 #ifdef NCURSES_WGETCH_EVENTS
238     if ((mode & 4) && evl) {
239         int c;
240
241         if (!result)
242             count = 0;
243
244         for (n = 0; n < evl->count; ++n) {
245             _nc_event *ev = evl->events[n];
246
247             if (ev->type == _NC_EVENT_FILE
248                 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) {
249                 ev->data.fev.result = 0;
250                 for (c = 0; c < count; c++)
251                     if (fds[c].fd == ev->data.fev.fd
252                         && fds[c].revents & POLLIN) {
253                         ev->data.fev.result |= _NC_EVENT_FILE_READABLE;
254                         evl->result_flags |= _NC_EVENT_FILE_READABLE;
255                     }
256             } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC
257                        && !result && timeout_is_event) {
258                 evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC;
259             }
260         }
261     }
262
263     if (fds != fd_list)
264         free((char *) fds);
265
266 #endif
267
268 #elif defined(__BEOS__)
269     /*
270      * BeOS's select() is declared in socket.h, so the configure script does
271      * not see it.  That's just as well, since that function works only for
272      * sockets.  This (using snooze and ioctl) was distilled from Be's patch
273      * for ncurses which uses a separate thread to simulate select().
274      *
275      * FIXME: the return values from the ioctl aren't very clear if we get
276      * interrupted.
277      *
278      * FIXME: this assumes mode&1 if milliseconds < 0 (see lib_getch.c).
279      */
280     result = 0;
281     if (mode & 1) {
282         int step = (milliseconds < 0) ? 0 : 5000;
283         bigtime_t d;
284         bigtime_t useconds = milliseconds * 1000;
285         int n, howmany;
286
287         if (useconds <= 0)      /* we're here to go _through_ the loop */
288             useconds = 1;
289
290         for (d = 0; d < useconds; d += step) {
291             n = 0;
292             howmany = ioctl(0, 'ichr', &n);
293             if (howmany >= 0 && n > 0) {
294                 result = 1;
295                 break;
296             }
297             if (useconds > 1 && step > 0) {
298                 snooze(step);
299                 milliseconds -= (step / 1000);
300                 if (milliseconds <= 0) {
301                     milliseconds = 0;
302                     break;
303                 }
304             }
305         }
306     } else if (milliseconds > 0) {
307         snooze(milliseconds * 1000);
308         milliseconds = 0;
309     }
310 #elif HAVE_SELECT
311     /*
312      * select() modifies the fd_set arguments; do this in the
313      * loop.
314      */
315     FD_ZERO(&set);
316
317     if (mode & 1) {
318         FD_SET(SP->_ifd, &set);
319         count = SP->_ifd + 1;
320     }
321     if ((mode & 2)
322         && (fd = SP->_mouse_fd) >= 0) {
323         FD_SET(fd, &set);
324         count = max(fd, count) + 1;
325     }
326 #ifdef NCURSES_WGETCH_EVENTS
327     if ((mode & 4) && evl) {
328         for (n = 0; n < evl->count; ++n) {
329             _nc_event *ev = evl->events[n];
330
331             if (ev->type == _NC_EVENT_FILE
332                 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) {
333                 FD_SET(ev->data.fev.fd, &set);
334                 count = max(ev->data.fev.fd + 1, count);
335             }
336         }
337     }
338 #endif
339
340     if (milliseconds >= 0) {
341         struct timeval ntimeout;
342         ntimeout.tv_sec = milliseconds / 1000;
343         ntimeout.tv_usec = (milliseconds % 1000) * 1000;
344         result = select(count, &set, NULL, NULL, &ntimeout);
345     } else {
346         result = select(count, &set, NULL, NULL, NULL);
347     }
348
349 #ifdef NCURSES_WGETCH_EVENTS
350     if ((mode & 4) && evl) {
351         evl->result_flags = 0;
352         for (n = 0; n < evl->count; ++n) {
353             _nc_event *ev = evl->events[n];
354
355             if (ev->type == _NC_EVENT_FILE
356                 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) {
357                 ev->data.fev.result = 0;
358                 if (FD_ISSET(ev->data.fev.fd, &set)) {
359                     ev->data.fev.result |= _NC_EVENT_FILE_READABLE;
360                     evl->result_flags |= _NC_EVENT_FILE_READABLE;
361                 }
362             } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC
363                        && !result && timeout_is_event)
364                 evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC;
365         }
366     }
367 #endif
368
369 #endif /* USE_FUNC_POLL, etc */
370
371     returntime = _nc_gettime(&t0, FALSE);
372
373     if (milliseconds >= 0)
374         milliseconds -= (returntime - starttime);
375
376 #ifdef NCURSES_WGETCH_EVENTS
377     if (evl) {
378         evl->result_flags = 0;
379         for (n = 0; n < evl->count; ++n) {
380             _nc_event *ev = evl->events[n];
381
382             if (ev->type == _NC_EVENT_TIMEOUT_MSEC) {
383                 long diff = (returntime - starttime);
384                 if (ev->data.timeout_msec <= diff)
385                     ev->data.timeout_msec = 0;
386                 else
387                     ev->data.timeout_msec -= diff;
388             }
389
390         }
391     }
392 #endif
393
394 #if PRECISE_GETTIME && HAVE_NANOSLEEP
395     /*
396      * If the timeout hasn't expired, and we've gotten no data,
397      * this is probably a system where 'select()' needs to be left
398      * alone so that it can complete.  Make this process sleep,
399      * then come back for more.
400      */
401     if (result == 0 && milliseconds > 100) {
402         napms(100);             /* FIXME: this won't be right if I recur! */
403         milliseconds -= 100;
404         goto retry;
405     }
406 #endif
407
408     /* return approximate time left in milliseconds */
409     if (timeleft)
410         *timeleft = milliseconds;
411
412     TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
413                       result, errno, milliseconds));
414
415     /*
416      * Both 'poll()' and 'select()' return the number of file descriptors
417      * that are active.  Translate this back to the mask that denotes which
418      * file-descriptors, so that we don't need all of this system-specific
419      * code everywhere.
420      */
421     if (result != 0) {
422         if (result > 0) {
423             result = 0;
424 #if USE_FUNC_POLL
425             for (count = 0; count < MIN_FDS; count++) {
426                 if ((mode & (1 << count))
427                     && (fds[count].revents & POLLIN)) {
428                     result |= (1 << count);
429                 }
430             }
431 #elif defined(__BEOS__)
432             result = 1;         /* redundant, but simple */
433 #elif HAVE_SELECT
434             if ((mode & 2)
435                 && (fd = SP->_mouse_fd) >= 0
436                 && FD_ISSET(fd, &set))
437                 result |= 2;
438             if ((mode & 1)
439                 && FD_ISSET(SP->_ifd, &set))
440                 result |= 1;
441 #endif
442         } else
443             result = 0;
444     }
445 #ifdef NCURSES_WGETCH_EVENTS
446     if ((mode & 4) && evl && evl->result_flags)
447         result |= 4;
448 #endif
449
450     return (result);
451 }