a21e2a16db0fa7ea140233d404d8365ae7b129cf
[ncurses.git] / ncurses / tty / lib_twait.c
1 /****************************************************************************
2  * Copyright (c) 1998,1999,2000 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 #ifdef __BEOS__
44 #include <OS.h>
45 #endif
46
47 #include <curses.priv.h>
48
49 #if USE_FUNC_POLL
50 # if HAVE_SYS_TIME_H
51 #  include <sys/time.h>
52 # endif
53 #elif HAVE_SELECT
54 # if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
55 #  include <sys/time.h>
56 # endif
57 # if HAVE_SYS_SELECT_H
58 #  include <sys/select.h>
59 # endif
60 #endif
61
62 MODULE_ID("$Id: lib_twait.c,v 1.37 2000/06/29 23:03:09 tom Exp $")
63
64 static long _nc_gettime(bool first)
65 {
66         long res;
67
68 #if HAVE_GETTIMEOFDAY
69 # define PRECISE_GETTIME 1
70         static struct timeval t0;
71         struct timeval t1;
72         gettimeofday(&t1, (struct timezone *)0);
73         if (first) {
74                 t0 = t1;
75         }
76         res = (t1.tv_sec  - t0.tv_sec)  * 1000
77             + (t1.tv_usec - t0.tv_usec) / 1000;
78 #else
79 # define PRECISE_GETTIME 0
80         static time_t t0;
81         time_t t1 = time((time_t*)0);
82         if (first) {
83                 t0 = t1;
84         }
85         res = (t1 - t0) * 1000;
86 #endif
87         T(("%s time: %ld msec", first ? "get" : "elapsed", res));
88         return res;
89 }
90
91 /*
92  * Wait a specified number of milliseconds, returning nonzero if the timer
93  * didn't expire before there is activity on the specified file descriptors.
94  * The file-descriptors are specified by the mode:
95  *      0 - none (absolute time)
96  *      1 - ncurses' normal input-descriptor
97  *      2 - mouse descriptor, if any
98  *      3 - either input or mouse.
99  * We return a mask that corresponds to the mode (e.g., 2 for mouse activity).
100  *
101  * If the milliseconds given are -1, the wait blocks until activity on the
102  * descriptors.
103  */
104 int _nc_timed_wait(int mode, int milliseconds, int *timeleft)
105 {
106 int     fd;
107 int     count;
108
109 int result;
110
111 #if USE_FUNC_POLL
112 struct pollfd fds[2];
113 #elif defined(__BEOS__)
114 #elif HAVE_SELECT
115 static fd_set set;
116 #endif
117
118 long starttime, returntime;
119
120         T(("start twait: %d milliseconds, mode: %d", milliseconds, mode));
121
122 #if PRECISE_GETTIME
123 retry:
124 #endif
125         starttime = _nc_gettime(TRUE);
126
127         count = 0;
128
129 #if USE_FUNC_POLL
130         if (mode & 1) {
131                 fds[count].fd     = SP->_ifd;
132                 fds[count].events = POLLIN;
133                 count++;
134         }
135         if ((mode & 2)
136          && (fd = SP->_mouse_fd) >= 0) {
137                 fds[count].fd     = fd;
138                 fds[count].events = POLLIN;
139                 count++;
140         }
141         result = poll(fds, count, milliseconds);
142
143 #elif defined(__BEOS__)
144         /*
145          * BeOS's select() is declared in socket.h, so the configure script does
146          * not see it.  That's just as well, since that function works only for
147          * sockets.  This (using snooze and ioctl) was distilled from Be's patch
148          * for ncurses which uses a separate thread to simulate select().
149          *
150          * FIXME: the return values from the ioctl aren't very clear if we get
151          * interrupted.
152          */
153         result = 0;
154         if (mode & 1) {
155                 bigtime_t d;
156                 bigtime_t useconds = milliseconds * 1000;
157                 int n, howmany;
158
159                 if (useconds == 0) /* we're here to go _through_ the loop */
160                         useconds = 1;
161
162                 for (d = 0; d < useconds; d += 5000) {
163                         n = 0;
164                         howmany = ioctl(0, 'ichr', &n);
165                         if (howmany >= 0 && n > 0) {
166                                 result = 1;
167                                 break;
168                         }
169                         if (useconds > 1)
170                                 snooze(5000);
171                         milliseconds -= 5;
172                 }
173         } else if (milliseconds > 0) {
174                 snooze(milliseconds * 1000);
175                 milliseconds = 0;
176         }
177 #elif HAVE_SELECT
178         /*
179          * select() modifies the fd_set arguments; do this in the
180          * loop.
181          */
182         FD_ZERO(&set);
183
184         if (mode & 1) {
185                 FD_SET(SP->_ifd, &set);
186                 count = SP->_ifd + 1;
187         }
188         if ((mode & 2)
189          && (fd = SP->_mouse_fd) >= 0) {
190                 FD_SET(fd, &set);
191                 count = max(fd, count) + 1;
192         }
193
194         if (milliseconds >= 0) {
195                 struct timeval ntimeout;
196                 ntimeout.tv_sec  = milliseconds / 1000;
197                 ntimeout.tv_usec = (milliseconds % 1000) * 1000;
198                 result = select(count, &set, NULL, NULL, &ntimeout);
199         } else {
200                 result = select(count, &set, NULL, NULL, NULL);
201         }
202 #endif
203
204         returntime = _nc_gettime(FALSE);
205
206         if (milliseconds >= 0)
207                 milliseconds -= (returntime - starttime);
208
209 #if PRECISE_GETTIME
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 && milliseconds > 100) {
217                 napms(100);
218                 milliseconds -= 100;
219                 goto retry;
220         }
221 #endif
222
223         /* return approximate time left in milliseconds */
224         if (timeleft)
225                 *timeleft = milliseconds;
226
227         T(("end twait: returned %d (%d), remaining time %d msec",
228                 result, errno, milliseconds));
229
230         /*
231          * Both 'poll()' and 'select()' return the number of file descriptors
232          * that are active.  Translate this back to the mask that denotes which
233          * file-descriptors, so that we don't need all of this system-specific
234          * code everywhere.
235          */
236         if (result != 0) {
237                 if (result > 0) {
238                         result = 0;
239 #if USE_FUNC_POLL
240                         for (count = 0; count < 2; count++) {
241                                 if ((mode & (1 << count))
242                                  && (fds[count].revents & POLLIN)) {
243                                         result |= (1 << count);
244                                 }
245                         }
246 #elif defined(__BEOS__)
247                         result = 1;     /* redundant, but simple */
248 #elif HAVE_SELECT
249                         if ((mode & 2)
250                          && (fd = SP->_mouse_fd) >= 0
251                          && FD_ISSET(fd, &set))
252                                 result |= 2;
253                         if ((mode & 1)
254                          && FD_ISSET(SP->_ifd, &set))
255                                 result |= 1;
256 #endif
257                 }
258                 else
259                         result = 0;
260         }
261
262         return (result);
263 }