f095c9b53c0c8c5e6c04c7ab56b764d14bb131f5
[ncurses.git] / ncurses / tty / 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 #ifdef __BEOS__
44 #include <OS.h>
45 #endif
46
47 #include <curses.priv.h>
48
49 #if USE_FUNC_POLL
50 # include <stropts.h>
51 # include <poll.h>
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.34 1999/10/16 21:25:10 tom Exp $")
65
66 static long _nc_gettime(bool first)
67 {
68         long res;
69
70 #if HAVE_GETTIMEOFDAY
71 # define PRECISE_GETTIME 1
72         static struct timeval t0;
73         struct timeval t1;
74         gettimeofday(&t1, (struct timezone *)0);
75         if (first) {
76                 t0 = t1;
77         }
78         res = (t1.tv_sec  - t0.tv_sec)  * 1000
79             + (t1.tv_usec - t0.tv_usec) / 1000;
80 #else
81 # define PRECISE_GETTIME 0
82         static time_t t0;
83         time_t t1 = time((time_t*)0);
84         if (first) {
85                 t0 = t1;
86         }
87         res = (t1 - t0) * 1000;
88 #endif
89         T(("%s time: %ld msec", first ? "get" : "elapsed", res));
90         return res;
91 }
92
93 /*
94  * Wait a specified number of milliseconds, returning nonzero if the timer
95  * didn't expire before there is activity on the specified file descriptors.
96  * The file-descriptors are specified by the mode:
97  *      0 - none (absolute time)
98  *      1 - ncurses' normal input-descriptor
99  *      2 - mouse descriptor, if any
100  *      3 - either input or mouse.
101  * We return a mask that corresponds to the mode (e.g., 2 for mouse activity).
102  *
103  * If the milliseconds given are -1, the wait blocks until activity on the
104  * descriptors.
105  */
106 int _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         if (mode & 1) {
133                 fds[count].fd     = SP->_ifd;
134                 fds[count].events = POLLIN;
135                 count++;
136         }
137         if ((mode & 2)
138          && (fd = SP->_mouse_fd) >= 0) {
139                 fds[count].fd     = fd;
140                 fds[count].events = POLLIN;
141                 count++;
142         }
143         result = poll(fds, count, milliseconds);
144
145 #elif defined(__BEOS__)
146         /*
147          * BeOS's select() is declared in socket.h, so the configure script does
148          * not see it.  That's just as well, since that function works only for
149          * sockets.  This (using snooze and ioctl) was distilled from Be's patch
150          * for ncurses which uses a separate thread to simulate select().
151          *
152          * FIXME: the return values from the ioctl aren't very clear if we get
153          * interrupted.
154          */
155         result = 0;
156         if (mode & 1) {
157                 bigtime_t d;
158                 bigtime_t useconds = milliseconds * 1000;
159                 int n, howmany;
160
161                 if (useconds == 0) /* we're here to go _through_ the loop */
162                         useconds = 1;
163
164                 for (d = 0; d < useconds; d += 5000) {
165                         n = 0;
166                         howmany = ioctl(0, 'ichr', &n);
167                         if (howmany >= 0 && n > 0) {
168                                 result = 1;
169                                 break;
170                         }
171                         if (useconds > 1)
172                                 snooze(5000);
173                         milliseconds -= 5;
174                 }
175         } else if (milliseconds > 0) {
176                 snooze(milliseconds * 1000);
177                 milliseconds = 0;
178         }
179 #elif HAVE_SELECT
180         /*
181          * select() modifies the fd_set arguments; do this in the
182          * loop.
183          */
184         FD_ZERO(&set);
185
186         if (mode & 1) {
187                 FD_SET(SP->_ifd, &set);
188                 count = SP->_ifd + 1;
189         }
190         if ((mode & 2)
191          && (fd = SP->_mouse_fd) >= 0) {
192                 FD_SET(fd, &set);
193                 count = max(fd, count) + 1;
194         }
195
196         if (milliseconds >= 0) {
197                 struct timeval ntimeout;
198                 ntimeout.tv_sec  = milliseconds / 1000;
199                 ntimeout.tv_usec = (milliseconds % 1000) * 1000;
200                 result = select(count, &set, NULL, NULL, &ntimeout);
201         } else {
202                 result = select(count, &set, NULL, NULL, NULL);
203         }
204 #endif
205
206         returntime = _nc_gettime(FALSE);
207
208         if (milliseconds >= 0)
209                 milliseconds -= (returntime - starttime);
210
211 #if PRECISE_GETTIME
212         /*
213          * If the timeout hasn't expired, and we've gotten no data,
214          * this is probably a system where 'select()' needs to be left
215          * alone so that it can complete.  Make this process sleep,
216          * then come back for more.
217          */
218         if (result == 0 && milliseconds > 100) {
219                 napms(100);
220                 milliseconds -= 100;
221                 goto retry;
222         }
223 #endif
224
225         /* return approximate time left in milliseconds */
226         if (timeleft)
227                 *timeleft = milliseconds;
228
229         T(("end twait: returned %d (%d), remaining time %d msec",
230                 result, errno, milliseconds));
231
232         /*
233          * Both 'poll()' and 'select()' return the number of file descriptors
234          * that are active.  Translate this back to the mask that denotes which
235          * file-descriptors, so that we don't need all of this system-specific
236          * code everywhere.
237          */
238         if (result != 0) {
239                 if (result > 0) {
240                         result = 0;
241 #if USE_FUNC_POLL
242                         for (count = 0; count < 2; count++) {
243                                 if ((mode & (1 << count))
244                                  && (fds[count].revents & POLLIN)) {
245                                         result |= (1 << count);
246                                         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                 }
261                 else
262                         result = 0;
263         }
264
265         return (result);
266 }