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