lib_twait.c revision 166124
1/****************************************************************************
2 * Copyright (c) 1998-2004,2006 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#include <curses.priv.h>
44
45#ifdef __BEOS__
46#undef false
47#undef true
48#include <OS.h>
49#endif
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
64MODULE_ID("$Id: lib_twait.c,v 1.51 2006/05/27 21:57:43 tom Exp $")
65
66static 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	res = 0;
79    } else {
80	/* .tv_sec and .tv_usec are unsigned, be careful when subtracting */
81	if (t0.tv_usec > t1.tv_usec) {	/* Convert 1s in 1e6 microsecs */
82	    t1.tv_usec += 1000000;
83	    t1.tv_sec--;
84	}
85	res = (t1.tv_sec - t0.tv_sec) * 1000
86	    + (t1.tv_usec - t0.tv_usec) / 1000;
87    }
88#else
89# define PRECISE_GETTIME 0
90    static time_t t0;
91    time_t t1 = time((time_t *) 0);
92    if (first) {
93	t0 = t1;
94    }
95    res = (t1 - t0) * 1000;
96#endif
97    TR(TRACE_IEVENT, ("%s time: %ld msec", first ? "get" : "elapsed", res));
98    return res;
99}
100
101#ifdef NCURSES_WGETCH_EVENTS
102NCURSES_EXPORT(int)
103_nc_eventlist_timeout(_nc_eventlist * evl)
104{
105    int event_delay = -1;
106    int n;
107
108    if (evl != 0) {
109
110	for (n = 0; n < evl->count; ++n) {
111	    _nc_event *ev = evl->events[n];
112
113	    if (ev->type == _NC_EVENT_TIMEOUT_MSEC) {
114		event_delay = ev->data.timeout_msec;
115		if (event_delay < 0)
116		    event_delay = INT_MAX;	/* FIXME Is this defined? */
117	    }
118	}
119    }
120    return event_delay;
121}
122#endif /* NCURSES_WGETCH_EVENTS */
123
124/*
125 * Wait a specified number of milliseconds, returning nonzero if the timer
126 * didn't expire before there is activity on the specified file descriptors.
127 * The file-descriptors are specified by the mode:
128 *	0 - none (absolute time)
129 *	1 - ncurses' normal input-descriptor
130 *	2 - mouse descriptor, if any
131 *	3 - either input or mouse.
132 *
133 * Experimental:  if NCURSES_WGETCH_EVENTS is defined, (mode & 4) determines
134 * whether to pay attention to evl argument.  If set, the smallest of
135 * millisecond and of timeout of evl is taken.
136 *
137 * We return a mask that corresponds to the mode (e.g., 2 for mouse activity).
138 *
139 * If the milliseconds given are -1, the wait blocks until activity on the
140 * descriptors.
141 */
142NCURSES_EXPORT(int)
143_nc_timed_wait(int mode,
144	       int milliseconds,
145	       int *timeleft
146	       EVENTLIST_2nd(_nc_eventlist * evl))
147{
148    int fd;
149    int count;
150    int result;
151
152#ifdef NCURSES_WGETCH_EVENTS
153    int timeout_is_event = 0;
154    int n;
155#endif
156
157#if USE_FUNC_POLL
158#define MIN_FDS 2
159    struct pollfd fd_list[MIN_FDS];
160    struct pollfd *fds = fd_list;
161#elif defined(__BEOS__)
162#elif HAVE_SELECT
163    static fd_set set;
164#endif
165
166    long starttime, returntime;
167
168    TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
169		      milliseconds, mode));
170
171#ifdef NCURSES_WGETCH_EVENTS
172    if (mode & 4) {
173	int event_delay = _nc_eventlist_timeout(evl);
174
175	if (event_delay >= 0
176	    && (milliseconds >= event_delay || milliseconds < 0)) {
177	    milliseconds = event_delay;
178	    timeout_is_event = 1;
179	}
180    }
181#endif
182
183#if PRECISE_GETTIME
184  retry:
185#endif
186    starttime = _nc_gettime(TRUE);
187
188    count = 0;
189
190#ifdef NCURSES_WGETCH_EVENTS
191    if ((mode & 4) && evl)
192	evl->result_flags = 0;
193#endif
194
195#if USE_FUNC_POLL
196    memset(fd_list, 0, sizeof(fd_list));
197
198#ifdef NCURSES_WGETCH_EVENTS
199    if ((mode & 4) && evl)
200	fds = typeMalloc(struct pollfd, MIN_FDS + evl->count);
201#endif
202
203    if (mode & 1) {
204	fds[count].fd = SP->_ifd;
205	fds[count].events = POLLIN;
206	count++;
207    }
208    if ((mode & 2)
209	&& (fd = SP->_mouse_fd) >= 0) {
210	fds[count].fd = fd;
211	fds[count].events = POLLIN;
212	count++;
213    }
214#ifdef NCURSES_WGETCH_EVENTS
215    if ((mode & 4) && evl) {
216	for (n = 0; n < evl->count; ++n) {
217	    _nc_event *ev = evl->events[n];
218
219	    if (ev->type == _NC_EVENT_FILE
220		&& (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) {
221		fds[count].fd = ev->data.fev.fd;
222		fds[count].events = POLLIN;
223		count++;
224	    }
225	}
226    }
227#endif
228
229    result = poll(fds, (unsigned) count, milliseconds);
230
231#ifdef NCURSES_WGETCH_EVENTS
232    if ((mode & 4) && evl) {
233	int c;
234
235	if (!result)
236	    count = 0;
237
238	for (n = 0; n < evl->count; ++n) {
239	    _nc_event *ev = evl->events[n];
240
241	    if (ev->type == _NC_EVENT_FILE
242		&& (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) {
243		ev->data.fev.result = 0;
244		for (c = 0; c < count; c++)
245		    if (fds[c].fd == ev->data.fev.fd
246			&& fds[c].revents & POLLIN) {
247			ev->data.fev.result |= _NC_EVENT_FILE_READABLE;
248			evl->result_flags |= _NC_EVENT_FILE_READABLE;
249		    }
250	    } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC
251		       && !result && timeout_is_event) {
252		evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC;
253	    }
254	}
255    }
256
257    if (fds != fd_list)
258	free((char *) fds);
259
260#endif
261
262#elif defined(__BEOS__)
263    /*
264     * BeOS's select() is declared in socket.h, so the configure script does
265     * not see it.  That's just as well, since that function works only for
266     * sockets.  This (using snooze and ioctl) was distilled from Be's patch
267     * for ncurses which uses a separate thread to simulate select().
268     *
269     * FIXME: the return values from the ioctl aren't very clear if we get
270     * interrupted.
271     *
272     * FIXME: this assumes mode&1 if milliseconds < 0 (see lib_getch.c).
273     */
274    result = 0;
275    if (mode & 1) {
276	int step = (milliseconds < 0) ? 0 : 5000;
277	bigtime_t d;
278	bigtime_t useconds = milliseconds * 1000;
279	int n, howmany;
280
281	if (useconds <= 0)	/* we're here to go _through_ the loop */
282	    useconds = 1;
283
284	for (d = 0; d < useconds; d += step) {
285	    n = 0;
286	    howmany = ioctl(0, 'ichr', &n);
287	    if (howmany >= 0 && n > 0) {
288		result = 1;
289		break;
290	    }
291	    if (useconds > 1 && step > 0) {
292		snooze(step);
293		milliseconds -= (step / 1000);
294		if (milliseconds <= 0) {
295		    milliseconds = 0;
296		    break;
297		}
298	    }
299	}
300    } else if (milliseconds > 0) {
301	snooze(milliseconds * 1000);
302	milliseconds = 0;
303    }
304#elif HAVE_SELECT
305    /*
306     * select() modifies the fd_set arguments; do this in the
307     * loop.
308     */
309    FD_ZERO(&set);
310
311    if (mode & 1) {
312	FD_SET(SP->_ifd, &set);
313	count = SP->_ifd + 1;
314    }
315    if ((mode & 2)
316	&& (fd = SP->_mouse_fd) >= 0) {
317	FD_SET(fd, &set);
318	count = max(fd, count) + 1;
319    }
320#ifdef NCURSES_WGETCH_EVENTS
321    if ((mode & 4) && evl) {
322	for (n = 0; n < evl->count; ++n) {
323	    _nc_event *ev = evl->events[n];
324
325	    if (ev->type == _NC_EVENT_FILE
326		&& (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) {
327		FD_SET(ev->data.fev.fd, &set);
328		count = max(ev->data.fev.fd + 1, count);
329	    }
330	}
331    }
332#endif
333
334    if (milliseconds >= 0) {
335	struct timeval ntimeout;
336	ntimeout.tv_sec = milliseconds / 1000;
337	ntimeout.tv_usec = (milliseconds % 1000) * 1000;
338	result = select(count, &set, NULL, NULL, &ntimeout);
339    } else {
340	result = select(count, &set, NULL, NULL, NULL);
341    }
342
343#ifdef NCURSES_WGETCH_EVENTS
344    if ((mode & 4) && evl) {
345	evl->result_flags = 0;
346	for (n = 0; n < evl->count; ++n) {
347	    _nc_event *ev = evl->events[n];
348
349	    if (ev->type == _NC_EVENT_FILE
350		&& (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) {
351		ev->data.fev.result = 0;
352		if (FD_ISSET(ev->data.fev.fd, &set)) {
353		    ev->data.fev.result |= _NC_EVENT_FILE_READABLE;
354		    evl->result_flags |= _NC_EVENT_FILE_READABLE;
355		}
356	    } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC
357		       && !result && timeout_is_event)
358		evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC;
359	}
360    }
361#endif
362
363#endif /* USE_FUNC_POLL, etc */
364
365    returntime = _nc_gettime(FALSE);
366
367    if (milliseconds >= 0)
368	milliseconds -= (returntime - starttime);
369
370#ifdef NCURSES_WGETCH_EVENTS
371    if (evl) {
372	evl->result_flags = 0;
373	for (n = 0; n < evl->count; ++n) {
374	    _nc_event *ev = evl->events[n];
375
376	    if (ev->type == _NC_EVENT_TIMEOUT_MSEC) {
377		long diff = (returntime - starttime);
378		if (ev->data.timeout_msec <= diff)
379		    ev->data.timeout_msec = 0;
380		else
381		    ev->data.timeout_msec -= diff;
382	    }
383
384	}
385    }
386#endif
387
388#if PRECISE_GETTIME && HAVE_NANOSLEEP
389    /*
390     * If the timeout hasn't expired, and we've gotten no data,
391     * this is probably a system where 'select()' needs to be left
392     * alone so that it can complete.  Make this process sleep,
393     * then come back for more.
394     */
395    if (result == 0 && milliseconds > 100) {
396	napms(100);		/* FIXME: this won't be right if I recur! */
397	milliseconds -= 100;
398	goto retry;
399    }
400#endif
401
402    /* return approximate time left in milliseconds */
403    if (timeleft)
404	*timeleft = milliseconds;
405
406    TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
407		      result, errno, milliseconds));
408
409    /*
410     * Both 'poll()' and 'select()' return the number of file descriptors
411     * that are active.  Translate this back to the mask that denotes which
412     * file-descriptors, so that we don't need all of this system-specific
413     * code everywhere.
414     */
415    if (result != 0) {
416	if (result > 0) {
417	    result = 0;
418#if USE_FUNC_POLL
419	    for (count = 0; count < MIN_FDS; count++) {
420		if ((mode & (1 << count))
421		    && (fds[count].revents & POLLIN)) {
422		    result |= (1 << count);
423		}
424	    }
425#elif defined(__BEOS__)
426	    result = 1;		/* redundant, but simple */
427#elif HAVE_SELECT
428	    if ((mode & 2)
429		&& (fd = SP->_mouse_fd) >= 0
430		&& FD_ISSET(fd, &set))
431		result |= 2;
432	    if ((mode & 1)
433		&& FD_ISSET(SP->_ifd, &set))
434		result |= 1;
435#endif
436	} else
437	    result = 0;
438    }
439#ifdef NCURSES_WGETCH_EVENTS
440    if ((mode & 4) && evl && evl->result_flags)
441	result |= 4;
442#endif
443
444    return (result);
445}
446