ncurses 4.1
[ncurses.git] / ncurses / lib_twait.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_twait.c
24 **
25 **      The routine _nc_timed_wait().
26 **
27 */
28
29 #include <curses.priv.h>
30
31 #if USE_FUNC_POLL
32 #include <stropts.h>
33 #include <poll.h>
34 #if HAVE_SYS_TIME_H
35 #include <sys/time.h>
36 #endif
37 #elif HAVE_SELECT
38 /* on SCO, <sys/time.h> conflicts with <sys/select.h> */
39 #if HAVE_SYS_TIME_H && ! SYSTEM_LOOKS_LIKE_SCO
40 #include <sys/time.h>
41 #endif
42 #if HAVE_SYS_SELECT_H
43 #include <sys/select.h>
44 #endif
45 #endif
46
47 MODULE_ID("$Id: lib_twait.c,v 1.18 1997/02/15 18:27:51 tom Exp $")
48
49 /*
50  * We want to define GOOD_SELECT if the last argument of select(2) is
51  * modified to indicate time left.  The code will deal gracefully with
52  * the other case, this is just an optimization to reduce the number
53  * of system calls per input event.
54  *
55  * In general, expect System-V-like UNIXes to have this behavior and BSD-like
56  * ones to not have it.  Check your manual page.  If it doesn't explicitly
57  * say the last argument is modified, assume it's not.
58  *
59  * (We'd really like configure to autodetect this, but writing a proper test
60  * turns out to be hard.)
61  */
62
63 #if HAVE_GETTIMEOFDAY
64 #if (defined(TRACE) && !HAVE_USLEEP) || ! GOOD_SELECT
65 static void _nc_gettime(struct timeval *tp)
66 {
67         gettimeofday(tp, (struct timezone *)0);
68         T(("time: %ld.%06ld", tp->tv_sec, tp->tv_usec));
69 }
70 #endif
71 #endif
72
73 #if !HAVE_USLEEP
74 int _nc_usleep(unsigned int usec)
75 {
76 int code;
77 struct timeval tval;
78
79 #if defined(TRACE) && HAVE_GETTIMEOFDAY
80         _nc_gettime(&tval);
81 #endif
82 #if USE_FUNC_POLL
83         {
84         struct pollfd fds[1];
85         code = poll(fds, 0, usec / 1000);
86         }
87 #elif HAVE_SELECT
88         tval.tv_sec = usec / 1000000;
89         tval.tv_usec = usec % 1000000;
90         code = select(0, NULL, NULL, NULL, &tval);
91 #endif
92
93 #if defined(TRACE) && HAVE_GETTIMEOFDAY
94         _nc_gettime(&tval);
95 #endif
96         return code;
97 }
98 #endif /* !HAVE_USLEEP */
99
100 /*
101  * Wait a specified number of milliseconds, returning true if the timer
102  * didn't expire before there is activity on the specified file descriptors.
103  * The file-descriptors are specified by the mode:
104  *      0 - none (absolute time)
105  *      1 - ncurses' normal input-descriptor
106  *      2 - mouse descriptor, if any
107  *      3 - either input or mouse.
108  *
109  * If the milliseconds given are -1, the wait blocks until activity on the
110  * descriptors.
111  */
112 int _nc_timed_wait(int mode, int milliseconds, int *timeleft)
113 {
114 int     fd;
115 int     count = 0;
116 long    whole_secs = milliseconds / 1000;
117 long    micro_secs = (milliseconds % 1000) * 1000;
118
119 int result = 0;
120 struct timeval ntimeout;
121
122 #if USE_FUNC_POLL
123 struct pollfd fds[2];
124 #elif HAVE_SELECT
125 static fd_set set;
126 #endif
127
128 #if !GOOD_SELECT && HAVE_GETTIMEOFDAY
129 struct timeval starttime, returntime;
130 long delta;
131
132         _nc_gettime(&starttime);
133 #endif
134
135         if (milliseconds >= 0) {
136                 ntimeout.tv_sec  = whole_secs;
137                 ntimeout.tv_usec = micro_secs;
138         } else {
139                 ntimeout.tv_sec  = 0;
140                 ntimeout.tv_usec = 0;
141         }
142
143         T(("start twait: %lu.%06lu secs", (long) ntimeout.tv_sec, (long) ntimeout.tv_usec));
144
145         /*
146          * The do loop tries to make it look like we have restarting signals,
147          * even if we don't.
148          */
149         do {
150                 count = 0;
151 #if USE_FUNC_POLL
152
153                 if (mode & 1) {
154                         fds[count].fd     = SP->_ifd;
155                         fds[count].events = POLLIN;
156                         count++;
157                 }
158                 if ((mode & 2)
159                  && (fd = _nc_mouse_fd()) >= 0) {
160                         fds[count].fd     = fd;
161                         fds[count].events = POLLIN;
162                         count++;
163                 }
164
165                 result = poll(fds, count, milliseconds);
166 #elif HAVE_SELECT
167                 /*
168                  * Some systems modify the fd_set arguments; do this in the
169                  * loop.
170                  */
171                 FD_ZERO(&set);
172
173                 if (mode & 1) {
174                         FD_SET(SP->_ifd, &set);
175                         count = SP->_ifd + 1;
176                 }
177                 if ((mode & 2)
178                  && (fd = _nc_mouse_fd()) >= 0) {
179                         FD_SET(fd, &set);
180                         count = max(fd, count) + 1;
181                 }
182
183                 errno = 0;
184                 result = select(count, &set, NULL, NULL, milliseconds >= 0 ? &ntimeout : 0);
185 #endif
186
187 #if !GOOD_SELECT && HAVE_GETTIMEOFDAY
188                 _nc_gettime(&returntime);
189
190                 /* The contents of ntimeout aren't guaranteed after return from
191                  * 'select()', so we disregard its contents.  Also, note that
192                  * on some systems, tv_sec and tv_usec are unsigned.
193                  */
194                 ntimeout.tv_sec  = whole_secs;
195                 ntimeout.tv_usec = micro_secs;
196
197 #define DELTA(f) (long)ntimeout.f - (long)returntime.f + (long)starttime.f
198
199                 delta = DELTA(tv_sec);
200                 if (delta < 0)
201                         delta = 0;
202                 ntimeout.tv_sec = delta;
203
204                 delta = DELTA(tv_usec);
205                 while (delta < 0 && ntimeout.tv_sec != 0) {
206                         ntimeout.tv_sec--;
207                         delta += 1000000;
208                 }
209                 ntimeout.tv_usec = delta;
210                 if (delta < 0)
211                         ntimeout.tv_sec = ntimeout.tv_usec = 0;
212
213                 /*
214                  * If the timeout hasn't expired, and we've gotten no data,
215                  * this is probably a system where 'select()' needs to be left
216                  * alone so that it can complete.  Make this process sleep,
217                  * then come back for more.
218                  */
219                 if (result == 0
220                  && (ntimeout.tv_sec != 0 || ntimeout.tv_usec > 100000)) {
221                         napms(100);
222                         continue;
223                 }
224 #endif
225         } while (result == -1 && errno == EINTR);
226
227         /* return approximate time left on the ntimeout, in milliseconds */
228         if (timeleft)
229                 *timeleft = (ntimeout.tv_sec * 1000) + (ntimeout.tv_usec / 1000);
230
231         T(("end twait: returned %d, remaining time %lu.%06lu secs (%d msec)",
232                 result, (long) ntimeout.tv_sec, (long) ntimeout.tv_usec,
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 = _nc_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 }