xref: /openbsd-src/lib/libcurses/tty/lib_twait.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: lib_twait.c,v 1.8 2001/01/22 18:02:00 millert Exp $	*/
2 
3 /****************************************************************************
4  * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc.              *
5  *                                                                          *
6  * Permission is hereby granted, free of charge, to any person obtaining a  *
7  * copy of this software and associated documentation files (the            *
8  * "Software"), to deal in the Software without restriction, including      *
9  * without limitation the rights to use, copy, modify, merge, publish,      *
10  * distribute, distribute with modifications, sublicense, and/or sell       *
11  * copies of the Software, and to permit persons to whom the Software is    *
12  * furnished to do so, subject to the following conditions:                 *
13  *                                                                          *
14  * The above copyright notice and this permission notice shall be included  *
15  * in all copies or substantial portions of the Software.                   *
16  *                                                                          *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24  *                                                                          *
25  * Except as contained in this notice, the name(s) of the above copyright   *
26  * holders shall not be used in advertising or otherwise to promote the     *
27  * sale, use or other dealings in this Software without prior written       *
28  * authorization.                                                           *
29  ****************************************************************************/
30 
31 /****************************************************************************
32  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
33  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
34  ****************************************************************************/
35 
36 /*
37 **	lib_twait.c
38 **
39 **	The routine _nc_timed_wait().
40 **
41 **	(This file was originally written by Eric Raymond; however except for
42 **	comments, none of the original code remains - T.Dickey).
43 */
44 
45 #ifdef __BEOS__
46 #include <OS.h>
47 #endif
48 
49 #include <curses.priv.h>
50 
51 #if USE_FUNC_POLL
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("$From: lib_twait.c,v 1.41 2000/12/10 03:04:30 tom Exp $")
65 
66 static long
67 _nc_gettime(bool first)
68 {
69     long res;
70 
71 #if HAVE_GETTIMEOFDAY
72 # define PRECISE_GETTIME 1
73     static struct timeval t0;
74     struct timeval t1;
75     gettimeofday(&t1, (struct timezone *) 0);
76     if (first) {
77 	t0 = t1;
78     }
79     res = (t1.tv_sec - t0.tv_sec) * 1000
80 	+ (t1.tv_usec - t0.tv_usec) / 1000;
81 #else
82 # define PRECISE_GETTIME 0
83     static time_t t0;
84     time_t t1 = time((time_t *) 0);
85     if (first) {
86 	t0 = t1;
87     }
88     res = (t1 - t0) * 1000;
89 #endif
90     T(("%s time: %ld msec", first ? "get" : "elapsed", res));
91     return res;
92 }
93 
94 /*
95  * Wait a specified number of milliseconds, returning nonzero if the timer
96  * didn't expire before there is activity on the specified file descriptors.
97  * The file-descriptors are specified by the mode:
98  *	0 - none (absolute time)
99  *	1 - ncurses' normal input-descriptor
100  *	2 - mouse descriptor, if any
101  *	3 - either input or mouse.
102  * We return a mask that corresponds to the mode (e.g., 2 for mouse activity).
103  *
104  * If the milliseconds given are -1, the wait blocks until activity on the
105  * descriptors.
106  */
107 NCURSES_EXPORT(int)
108 _nc_timed_wait
109 (int mode, int milliseconds, int *timeleft)
110 {
111     int fd;
112     int count;
113 
114     int result;
115 
116 #if USE_FUNC_POLL
117     struct pollfd fds[2];
118 #elif defined(__BEOS__)
119 #elif HAVE_SELECT
120     static fd_set *set;
121     static size_t setsize;
122     size_t nsetsize;
123     int readfd;
124 #endif
125 
126     long starttime, returntime;
127 
128     T(("start twait: %d milliseconds, mode: %d", milliseconds, mode));
129 
130 #if PRECISE_GETTIME
131   retry:
132 #endif
133     starttime = _nc_gettime(TRUE);
134 
135     count = 0;
136 
137 #if USE_FUNC_POLL
138     memset(fds, 0, sizeof(fds));
139     if (mode & 1) {
140 	fds[count].fd = SP->_ifd;
141 	fds[count].events = POLLIN;
142 	count++;
143     }
144     if ((mode & 2)
145 	&& (fd = SP->_mouse_fd) >= 0) {
146 	fds[count].fd = fd;
147 	fds[count].events = POLLIN;
148 	count++;
149     }
150     result = poll(fds, count, milliseconds);
151 
152 #elif defined(__BEOS__)
153     /*
154      * BeOS's select() is declared in socket.h, so the configure script does
155      * not see it.  That's just as well, since that function works only for
156      * sockets.  This (using snooze and ioctl) was distilled from Be's patch
157      * for ncurses which uses a separate thread to simulate select().
158      *
159      * FIXME: the return values from the ioctl aren't very clear if we get
160      * interrupted.
161      */
162     result = 0;
163     if (mode & 1) {
164 	bigtime_t d;
165 	bigtime_t useconds = milliseconds * 1000;
166 	int n, howmany;
167 
168 	if (useconds == 0)	/* we're here to go _through_ the loop */
169 	    useconds = 1;
170 
171 	for (d = 0; d < useconds; d += 5000) {
172 	    n = 0;
173 	    howmany = ioctl(0, 'ichr', &n);
174 	    if (howmany >= 0 && n > 0) {
175 		result = 1;
176 		break;
177 	    }
178 	    if (useconds > 1)
179 		snooze(5000);
180 	    milliseconds -= 5;
181 	}
182     } else if (milliseconds > 0) {
183 	snooze(milliseconds * 1000);
184 	milliseconds = 0;
185     }
186 #elif HAVE_SELECT
187     if (mode & 1) {
188 	count = SP->_ifd;
189 	readfd = SP->_ifd;
190     }
191     if ((mode & 2) && (fd = SP->_mouse_fd) >= 0) {
192 	count = max(fd, count);
193 	readfd = fd;
194     }
195 
196     /*
197      * grow set as needed.
198      */
199     nsetsize = howmany(count, NFDBITS) * sizeof(fd_mask);
200     if (setsize == 0 || setsize < nsetsize) {
201 	setsize = nsetsize;
202 	set = _nc_doalloc(set, setsize);
203     }
204 
205     /*
206      * select() modifies the fd_set arguments; do this in the
207      * loop.
208      */
209     memset(set, 0, setsize);
210     FD_SET(readfd, set);
211 
212     if (milliseconds >= 0) {
213 	struct timeval ntimeout;
214 	ntimeout.tv_sec = milliseconds / 1000;
215 	ntimeout.tv_usec = (milliseconds % 1000) * 1000;
216 	result = select(count + 1, set, NULL, NULL, &ntimeout);
217     } else {
218 	result = select(count + 1, set, NULL, NULL, NULL);
219     }
220 #endif
221 
222     returntime = _nc_gettime(FALSE);
223 
224     if (milliseconds >= 0)
225 	milliseconds -= (returntime - starttime);
226 
227 #if PRECISE_GETTIME
228     /*
229      * If the timeout hasn't expired, and we've gotten no data,
230      * this is probably a system where 'select()' needs to be left
231      * alone so that it can complete.  Make this process sleep,
232      * then come back for more.
233      */
234     if (result == 0 && milliseconds > 100) {
235 	napms(100);
236 	milliseconds -= 100;
237 	goto retry;
238     }
239 #endif
240 
241     /* return approximate time left in milliseconds */
242     if (timeleft)
243 	*timeleft = milliseconds;
244 
245     T(("end twait: returned %d (%d), remaining time %d msec",
246        result, errno, milliseconds));
247 
248     /*
249      * Both 'poll()' and 'select()' return the number of file descriptors
250      * that are active.  Translate this back to the mask that denotes which
251      * file-descriptors, so that we don't need all of this system-specific
252      * code everywhere.
253      */
254     if (result != 0) {
255 	if (result > 0) {
256 	    result = 0;
257 #if USE_FUNC_POLL
258 	    for (count = 0; count < 2; count++) {
259 		if ((mode & (1 << count))
260 		    && (fds[count].revents & POLLIN)) {
261 		    result |= (1 << count);
262 		}
263 	    }
264 #elif defined(__BEOS__)
265 	    result = 1;		/* redundant, but simple */
266 #elif HAVE_SELECT
267 	    if ((mode & 2)
268 		&& (fd = SP->_mouse_fd) >= 0
269 		&& FD_ISSET(fd, set))
270 		result |= 2;
271 	    if ((mode & 1)
272 		&& FD_ISSET(SP->_ifd, set))
273 		result |= 1;
274 #endif
275 	} else
276 	    result = 0;
277     }
278 
279     return (result);
280 }
281