6d24d8ddc6cb3e83b34f2e4f439b262f012b0519
[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.39 2000/08/26 19:34:15 tom Exp $")
63
64 static long
65 _nc_gettime(bool first)
66 {
67     long res;
68
69 #if HAVE_GETTIMEOFDAY
70 # define PRECISE_GETTIME 1
71     static struct timeval t0;
72     struct timeval t1;
73     gettimeofday(&t1, (struct timezone *) 0);
74     if (first) {
75         t0 = t1;
76     }
77     res = (t1.tv_sec - t0.tv_sec) * 1000
78         + (t1.tv_usec - t0.tv_usec) / 1000;
79 #else
80 # define PRECISE_GETTIME 0
81     static time_t t0;
82     time_t t1 = time((time_t *) 0);
83     if (first) {
84         t0 = t1;
85     }
86     res = (t1 - t0) * 1000;
87 #endif
88     T(("%s time: %ld msec", first ? "get" : "elapsed", res));
89     return res;
90 }
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
106 _nc_timed_wait(int mode, int milliseconds, int *timeleft)
107 {
108     int fd;
109     int count;
110
111     int result;
112
113 #if USE_FUNC_POLL
114     struct pollfd fds[2];
115 #elif defined(__BEOS__)
116 #elif HAVE_SELECT
117     static fd_set set;
118 #endif
119
120     long starttime, returntime;
121
122     T(("start twait: %d milliseconds, mode: %d", milliseconds, mode));
123
124 #if PRECISE_GETTIME
125   retry:
126 #endif
127     starttime = _nc_gettime(TRUE);
128
129     count = 0;
130
131 #if USE_FUNC_POLL
132     memset(fds, 0, sizeof(fds));
133     if (mode & 1) {
134         fds[count].fd = SP->_ifd;
135         fds[count].events = POLLIN;
136         count++;
137     }
138     if ((mode & 2)
139         && (fd = SP->_mouse_fd) >= 0) {
140         fds[count].fd = fd;
141         fds[count].events = POLLIN;
142         count++;
143     }
144     result = poll(fds, count, milliseconds);
145
146 #elif defined(__BEOS__)
147     /*
148      * BeOS's select() is declared in socket.h, so the configure script does
149      * not see it.  That's just as well, since that function works only for
150      * sockets.  This (using snooze and ioctl) was distilled from Be's patch
151      * for ncurses which uses a separate thread to simulate select().
152      *
153      * FIXME: the return values from the ioctl aren't very clear if we get
154      * interrupted.
155      */
156     result = 0;
157     if (mode & 1) {
158         bigtime_t d;
159         bigtime_t useconds = milliseconds * 1000;
160         int n, howmany;
161
162         if (useconds == 0)      /* we're here to go _through_ the loop */
163             useconds = 1;
164
165         for (d = 0; d < useconds; d += 5000) {
166             n = 0;
167             howmany = ioctl(0, 'ichr', &n);
168             if (howmany >= 0 && n > 0) {
169                 result = 1;
170                 break;
171             }
172             if (useconds > 1)
173                 snooze(5000);
174             milliseconds -= 5;
175         }
176     } else if (milliseconds > 0) {
177         snooze(milliseconds * 1000);
178         milliseconds = 0;
179     }
180 #elif HAVE_SELECT
181     /*
182      * select() modifies the fd_set arguments; do this in the
183      * loop.
184      */
185     FD_ZERO(&set);
186
187     if (mode & 1) {
188         FD_SET(SP->_ifd, &set);
189         count = SP->_ifd + 1;
190     }
191     if ((mode & 2)
192         && (fd = SP->_mouse_fd) >= 0) {
193         FD_SET(fd, &set);
194         count = max(fd, count) + 1;
195     }
196
197     if (milliseconds >= 0) {
198         struct timeval ntimeout;
199         ntimeout.tv_sec = milliseconds / 1000;
200         ntimeout.tv_usec = (milliseconds % 1000) * 1000;
201         result = select(count, &set, NULL, NULL, &ntimeout);
202     } else {
203         result = select(count, &set, NULL, NULL, NULL);
204     }
205 #endif
206
207     returntime = _nc_gettime(FALSE);
208
209     if (milliseconds >= 0)
210         milliseconds -= (returntime - starttime);
211
212 #if PRECISE_GETTIME
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 && milliseconds > 100) {
220         napms(100);
221         milliseconds -= 100;
222         goto retry;
223     }
224 #endif
225
226     /* return approximate time left in milliseconds */
227     if (timeleft)
228         *timeleft = milliseconds;
229
230     T(("end twait: returned %d (%d), remaining time %d msec",
231        result, errno, milliseconds));
232
233     /*
234      * Both 'poll()' and 'select()' return the number of file descriptors
235      * that are active.  Translate this back to the mask that denotes which
236      * file-descriptors, so that we don't need all of this system-specific
237      * code everywhere.
238      */
239     if (result != 0) {
240         if (result > 0) {
241             result = 0;
242 #if USE_FUNC_POLL
243             for (count = 0; count < 2; count++) {
244                 if ((mode & (1 << count))
245                     && (fds[count].revents & POLLIN)) {
246                     result |= (1 << count);
247                 }
248             }
249 #elif defined(__BEOS__)
250             result = 1;         /* redundant, but simple */
251 #elif HAVE_SELECT
252             if ((mode & 2)
253                 && (fd = SP->_mouse_fd) >= 0
254                 && FD_ISSET(fd, &set))
255                 result |= 2;
256             if ((mode & 1)
257                 && FD_ISSET(SP->_ifd, &set))
258                 result |= 1;
259 #endif
260         } else
261             result = 0;
262     }
263
264     return (result);
265 }