ncurses 4.2
[ncurses.git] / ncurses / lib_twait.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_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 #if USE_FUNC_POLL
46 # include <stropts.h>
47 # include <poll.h>
48 # if HAVE_SYS_TIME_H
49 #  include <sys/time.h>
50 # endif
51 #elif HAVE_SELECT
52 # if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
53 #  include <sys/time.h>
54 # endif
55 # if HAVE_SYS_SELECT_H
56 #  include <sys/select.h>
57 # endif
58 #endif
59
60 #ifdef __BEOS__
61 /* BeOS select() only works on sockets.  Use the tty hack instead */
62 #include <socket.h>
63 #define select check_select
64 #endif
65
66 MODULE_ID("$Id: lib_twait.c,v 1.30 1998/02/11 12:14:01 tom Exp $")
67
68 /*
69  * We want to define GOOD_SELECT if the last argument of select(2) is
70  * modified to indicate time left.  The code will deal gracefully with
71  * the other case, this is just an optimization to reduce the number
72  * of system calls per input event.
73  *
74  * In general, expect System-V-like UNIXes to have this behavior and BSD-like
75  * ones to not have it.  Check your manual page.  If it doesn't explicitly
76  * say the last argument is modified, assume it's not.
77  *
78  * (We'd really like configure to autodetect this, but writing a proper test
79  * turns out to be hard.)
80  */
81
82 #if HAVE_GETTIMEOFDAY
83 #if (defined(TRACE) && !HAVE_USLEEP) || ! GOOD_SELECT
84 static void _nc_gettime(struct timeval *tp)
85 {
86         gettimeofday(tp, (struct timezone *)0);
87         T(("time: %ld.%06ld", (long) tp->tv_sec, (long) tp->tv_usec));
88 }
89 #endif
90 #endif
91
92 /*
93  * Wait a specified number of milliseconds, returning nonzero if the timer
94  * didn't expire before there is activity on the specified file descriptors.
95  * The file-descriptors are specified by the mode:
96  *      0 - none (absolute time)
97  *      1 - ncurses' normal input-descriptor
98  *      2 - mouse descriptor, if any
99  *      3 - either input or mouse.
100  * We return a mask that corresponds to the mode (e.g., 2 for mouse activity).
101  *
102  * If the milliseconds given are -1, the wait blocks until activity on the
103  * descriptors.
104  */
105 int _nc_timed_wait(int mode, int milliseconds, int *timeleft)
106 {
107 int     fd;
108 int     count = 0;
109 long    whole_secs = milliseconds / 1000;
110 long    micro_secs = (milliseconds % 1000) * 1000;
111
112 int result = 0;
113 struct timeval ntimeout;
114
115 #if USE_FUNC_POLL
116 struct pollfd fds[2];
117 #elif HAVE_SELECT
118 static fd_set set;
119 #endif
120
121 #if !GOOD_SELECT && HAVE_GETTIMEOFDAY
122 struct timeval starttime, returntime;
123 long delta;
124
125         _nc_gettime(&starttime);
126 #endif
127
128         if (milliseconds >= 0) {
129                 ntimeout.tv_sec  = whole_secs;
130                 ntimeout.tv_usec = micro_secs;
131         } else {
132                 ntimeout.tv_sec  = 0;
133                 ntimeout.tv_usec = 0;
134         }
135
136         T(("start twait: %lu.%06lu secs, mode: %d", (long) ntimeout.tv_sec, (long) ntimeout.tv_usec, mode));
137
138 #ifdef HIDE_EINTR
139         /*
140          * The do loop tries to make it look like we have restarting signals,
141          * even if we don't.
142          */
143         do {
144 #endif /* HIDE_EINTR */
145 #if !GOOD_SELECT && HAVE_GETTIMEOFDAY
146         retry:
147 #endif
148                 count = 0;
149 #if USE_FUNC_POLL
150
151                 if (mode & 1) {
152                         fds[count].fd     = SP->_ifd;
153                         fds[count].events = POLLIN;
154                         count++;
155                 }
156                 if ((mode & 2)
157                  && (fd = SP->_mouse_fd) >= 0) {
158                         fds[count].fd     = fd;
159                         fds[count].events = POLLIN;
160                         count++;
161                 }
162                 result = poll(fds, count, milliseconds);
163 #elif HAVE_SELECT
164                 /*
165                  * Some systems modify the fd_set arguments; do this in the
166                  * loop.
167                  */
168                 FD_ZERO(&set);
169
170                 if (mode & 1) {
171                         FD_SET(SP->_ifd, &set);
172                         count = SP->_ifd + 1;
173                 }
174                 if ((mode & 2)
175                  && (fd = SP->_mouse_fd) >= 0) {
176                         FD_SET(fd, &set);
177                         count = max(fd, count) + 1;
178                 }
179
180                 errno = 0;
181                 result = select(count, &set, NULL, NULL, milliseconds >= 0 ? &ntimeout : 0);
182 #endif
183
184 #if !GOOD_SELECT && HAVE_GETTIMEOFDAY
185                 _nc_gettime(&returntime);
186
187                 /* The contents of ntimeout aren't guaranteed after return from
188                  * 'select()', so we disregard its contents.  Also, note that
189                  * on some systems, tv_sec and tv_usec are unsigned.
190                  */
191                 ntimeout.tv_sec  = whole_secs;
192                 ntimeout.tv_usec = micro_secs;
193
194 #define DELTA(f) (long)ntimeout.f - (long)returntime.f + (long)starttime.f
195
196                 delta = DELTA(tv_sec);
197                 if (delta < 0)
198                         delta = 0;
199                 ntimeout.tv_sec = delta;
200
201                 delta = DELTA(tv_usec);
202                 while (delta < 0 && ntimeout.tv_sec != 0) {
203                         ntimeout.tv_sec--;
204                         delta += 1000000;
205                 }
206                 ntimeout.tv_usec = delta;
207                 if (delta < 0)
208                         ntimeout.tv_sec = ntimeout.tv_usec = 0;
209
210                 /*
211                  * If the timeout hasn't expired, and we've gotten no data,
212                  * this is probably a system where 'select()' needs to be left
213                  * alone so that it can complete.  Make this process sleep,
214                  * then come back for more.
215                  */
216                 if (result == 0
217                  && (ntimeout.tv_sec != 0 || ntimeout.tv_usec > 100000)) {
218                         napms(100);
219                         goto retry;
220                 }
221 #endif
222 #ifdef HIDE_EINTR
223         } while (result == -1 && errno == EINTR);
224 #endif
225
226         /* return approximate time left on the ntimeout, in milliseconds */
227         if (timeleft)
228                 *timeleft = (ntimeout.tv_sec * 1000) + (ntimeout.tv_usec / 1000);
229
230         T(("end twait: returned %d (%d), remaining time %lu.%06lu secs (%d msec)",
231                 result, errno,
232                 (long) ntimeout.tv_sec, (long) (ntimeout.tv_usec / 1000),
233                 timeleft ? *timeleft : -1));
234
235         /*
236          * Both 'poll()' and 'select()' return the number of file descriptors
237          * that are active.  Translate this back to the mask that denotes which
238          * file-descriptors, so that we don't need all of this system-specific
239          * code everywhere.
240          */
241         if (result != 0) {
242                 if (result > 0) {
243                         result = 0;
244 #if USE_FUNC_POLL
245                         for (count = 0; count < 2; count++) {
246                                 if ((mode & (1 << count))
247                                  && (fds[count].revents & POLLIN)) {
248                                         result |= (1 << count);
249                                         count++;
250                                 }
251                         }
252 #elif HAVE_SELECT
253                         if ((mode & 2)
254                          && (fd = SP->_mouse_fd) >= 0
255                          && FD_ISSET(fd, &set))
256                                 result |= 2;
257                         if ((mode & 1)
258                          && FD_ISSET(SP->_ifd, &set))
259                                 result |= 1;
260 #endif
261                 }
262                 else
263                         result = 0;
264         }
265
266         return (result);
267 }