1 /****************************************************************************
2 * Copyright (c) 1998-2014,2015 Free Software Foundation, Inc. *
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: *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
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. *
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 *
27 ****************************************************************************/
29 /****************************************************************************
30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
31 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
32 * and: Thomas E. Dickey 1996-on *
33 ****************************************************************************/
38 ** The routine _nc_timed_wait().
40 ** (This file was originally written by Eric Raymond; however except for
41 ** comments, none of the original code remains - T.Dickey).
44 #include <curses.priv.h>
46 #if defined __HAIKU__ && defined __BEOS__
63 # include <sys/time.h>
66 # if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
67 # include <sys/time.h>
69 # if HAVE_SYS_SELECT_H
70 # include <sys/select.h>
74 # include <sys/time.h>
78 MODULE_ID("$Id: lib_twait.c,v 1.70 2015/07/04 21:01:02 tom Exp $")
81 _nc_gettime(TimeType
* t0
, int first
)
87 gettimeofday(&t1
, (struct timezone
*) 0);
92 /* .tv_sec and .tv_usec are unsigned, be careful when subtracting */
93 if (t0
->tv_usec
> t1
.tv_usec
) {
94 t1
.tv_usec
+= 1000000; /* Convert 1s in 1e6 microsecs */
97 res
= (t1
.tv_sec
- t0
->tv_sec
) * 1000
98 + (t1
.tv_usec
- t0
->tv_usec
) / 1000;
101 time_t t1
= time((time_t *) 0);
105 res
= (long) ((t1
- *t0
) * 1000);
107 TR(TRACE_IEVENT
, ("%s time: %ld msec", first
? "get" : "elapsed", res
));
111 #ifdef NCURSES_WGETCH_EVENTS
113 _nc_eventlist_timeout(_nc_eventlist
* evl
)
115 int event_delay
= -1;
120 for (n
= 0; n
< evl
->count
; ++n
) {
121 _nc_event
*ev
= evl
->events
[n
];
123 if (ev
->type
== _NC_EVENT_TIMEOUT_MSEC
) {
124 event_delay
= (int) ev
->data
.timeout_msec
;
126 event_delay
= INT_MAX
; /* FIXME Is this defined? */
132 #endif /* NCURSES_WGETCH_EVENTS */
134 #if (USE_FUNC_POLL || HAVE_SELECT)
135 # define MAYBE_UNUSED
137 # define MAYBE_UNUSED GCC_UNUSED
140 #if (USE_FUNC_POLL || HAVE_SELECT)
141 # define MAYBE_UNUSED
143 # define MAYBE_UNUSED GCC_UNUSED
147 * Wait a specified number of milliseconds, returning nonzero if the timer
148 * didn't expire before there is activity on the specified file descriptors.
149 * The file-descriptors are specified by the mode:
150 * TW_NONE 0 - none (absolute time)
151 * TW_INPUT 1 - ncurses' normal input-descriptor
152 * TW_MOUSE 2 - mouse descriptor, if any
153 * TW_ANY 3 - either input or mouse.
155 * Experimental: if NCURSES_WGETCH_EVENTS is defined, (mode & 4) determines
156 * whether to pay attention to evl argument. If set, the smallest of
157 * millisecond and of timeout of evl is taken.
159 * We return a mask that corresponds to the mode (e.g., 2 for mouse activity).
161 * If the milliseconds given are -1, the wait blocks until activity on the
165 _nc_timed_wait(SCREEN
*sp MAYBE_UNUSED
,
166 int mode MAYBE_UNUSED
,
169 EVENTLIST_2nd(_nc_eventlist
* evl
))
172 int result
= TW_NONE
;
174 #if (USE_FUNC_POLL || HAVE_SELECT)
178 #ifdef NCURSES_WGETCH_EVENTS
179 int timeout_is_event
= 0;
185 struct pollfd fd_list
[MIN_FDS
];
186 struct pollfd
*fds
= fd_list
;
187 #elif defined(__BEOS__)
198 long starttime
, returntime
;
200 TR(TRACE_IEVENT
, ("start twait: %d milliseconds, mode: %d",
201 milliseconds
, mode
));
203 #ifdef NCURSES_WGETCH_EVENTS
204 if (mode
& TW_EVENT
) {
205 int event_delay
= _nc_eventlist_timeout(evl
);
208 && (milliseconds
>= event_delay
|| milliseconds
< 0)) {
209 milliseconds
= event_delay
;
210 timeout_is_event
= 1;
215 #if PRECISE_GETTIME && HAVE_NANOSLEEP
218 starttime
= _nc_gettime(&t0
, TRUE
);
223 #ifdef NCURSES_WGETCH_EVENTS
224 if ((mode
& TW_EVENT
) && evl
)
225 evl
->result_flags
= 0;
229 memset(fd_list
, 0, sizeof(fd_list
));
231 #ifdef NCURSES_WGETCH_EVENTS
232 if ((mode
& TW_EVENT
) && evl
) {
234 fds
= typeMalloc(struct pollfd
, MIN_FDS
+ evl
->count
);
240 if (mode
& TW_INPUT
) {
241 fds
[count
].fd
= sp
->_ifd
;
242 fds
[count
].events
= POLLIN
;
245 if ((mode
& TW_MOUSE
)
246 && (fd
= sp
->_mouse_fd
) >= 0) {
248 fds
[count
].events
= POLLIN
;
251 #ifdef NCURSES_WGETCH_EVENTS
252 if ((mode
& TW_EVENT
) && evl
) {
253 for (n
= 0; n
< evl
->count
; ++n
) {
254 _nc_event
*ev
= evl
->events
[n
];
256 if (ev
->type
== _NC_EVENT_FILE
257 && (ev
->data
.fev
.flags
& _NC_EVENT_FILE_READABLE
)) {
258 fds
[count
].fd
= ev
->data
.fev
.fd
;
259 fds
[count
].events
= POLLIN
;
266 result
= poll(fds
, (size_t) count
, milliseconds
);
268 #ifdef NCURSES_WGETCH_EVENTS
269 if ((mode
& TW_EVENT
) && evl
) {
275 for (n
= 0; n
< evl
->count
; ++n
) {
276 _nc_event
*ev
= evl
->events
[n
];
278 if (ev
->type
== _NC_EVENT_FILE
279 && (ev
->data
.fev
.flags
& _NC_EVENT_FILE_READABLE
)) {
280 ev
->data
.fev
.result
= 0;
281 for (c
= 0; c
< count
; c
++)
282 if (fds
[c
].fd
== ev
->data
.fev
.fd
283 && fds
[c
].revents
& POLLIN
) {
284 ev
->data
.fev
.result
|= _NC_EVENT_FILE_READABLE
;
285 evl
->result_flags
|= _NC_EVENT_FILE_READABLE
;
287 } else if (ev
->type
== _NC_EVENT_TIMEOUT_MSEC
288 && !result
&& timeout_is_event
) {
289 evl
->result_flags
|= _NC_EVENT_TIMEOUT_MSEC
;
295 #elif defined(__BEOS__)
297 * BeOS's select() is declared in socket.h, so the configure script does
298 * not see it. That's just as well, since that function works only for
299 * sockets. This (using snooze and ioctl) was distilled from Be's patch
300 * for ncurses which uses a separate thread to simulate select().
302 * FIXME: the return values from the ioctl aren't very clear if we get
305 * FIXME: this assumes mode&1 if milliseconds < 0 (see lib_getch.c).
308 if (mode
& TW_INPUT
) {
309 int step
= (milliseconds
< 0) ? 0 : 5000;
311 bigtime_t useconds
= milliseconds
* 1000;
314 if (useconds
<= 0) /* we're here to go _through_ the loop */
317 for (d
= 0; d
< useconds
; d
+= step
) {
319 howmany
= ioctl(0, 'ichr', &n
);
320 if (howmany
>= 0 && n
> 0) {
324 if (useconds
> 1 && step
> 0) {
326 milliseconds
-= (step
/ 1000);
327 if (milliseconds
<= 0) {
333 } else if (milliseconds
> 0) {
334 snooze(milliseconds
* 1000);
339 * select() modifies the fd_set arguments; do this in the
345 if (mode
& TW_INPUT
) {
346 FD_SET(sp
->_ifd
, &set
);
347 count
= sp
->_ifd
+ 1;
350 if ((mode
& TW_MOUSE
)
351 && (fd
= sp
->_mouse_fd
) >= 0) {
353 count
= max(fd
, count
) + 1;
355 #ifdef NCURSES_WGETCH_EVENTS
356 if ((mode
& TW_EVENT
) && evl
) {
357 for (n
= 0; n
< evl
->count
; ++n
) {
358 _nc_event
*ev
= evl
->events
[n
];
360 if (ev
->type
== _NC_EVENT_FILE
361 && (ev
->data
.fev
.flags
& _NC_EVENT_FILE_READABLE
)) {
362 FD_SET(ev
->data
.fev
.fd
, &set
);
363 count
= max(ev
->data
.fev
.fd
+ 1, count
);
370 for (saved_set
= set
;; set
= saved_set
) {
371 if ((mode
& TW_INPUT
)
372 && (sp
->_extended_key
373 || (KbdPeek(&ki
, 0) == 0
374 && (ki
.fbStatus
& KBDTRF_FINAL_CHAR_IN
)))) {
376 FD_SET(sp
->_ifd
, &set
);
382 tv
.tv_usec
= (milliseconds
== 0) ? 0 : (10 * 1000);
384 if ((result
= select(count
, &set
, NULL
, NULL
, &tv
)) != 0)
388 if (milliseconds
>= 0 && _nc_gettime(&t0
, FALSE
) >= milliseconds
) {
394 if (milliseconds
>= 0) {
395 struct timeval ntimeout
;
396 ntimeout
.tv_sec
= milliseconds
/ 1000;
397 ntimeout
.tv_usec
= (milliseconds
% 1000) * 1000;
398 result
= select(count
, &set
, NULL
, NULL
, &ntimeout
);
400 result
= select(count
, &set
, NULL
, NULL
, NULL
);
404 #ifdef NCURSES_WGETCH_EVENTS
405 if ((mode
& TW_EVENT
) && evl
) {
406 evl
->result_flags
= 0;
407 for (n
= 0; n
< evl
->count
; ++n
) {
408 _nc_event
*ev
= evl
->events
[n
];
410 if (ev
->type
== _NC_EVENT_FILE
411 && (ev
->data
.fev
.flags
& _NC_EVENT_FILE_READABLE
)) {
412 ev
->data
.fev
.result
= 0;
413 if (FD_ISSET(ev
->data
.fev
.fd
, &set
)) {
414 ev
->data
.fev
.result
|= _NC_EVENT_FILE_READABLE
;
415 evl
->result_flags
|= _NC_EVENT_FILE_READABLE
;
417 } else if (ev
->type
== _NC_EVENT_TIMEOUT_MSEC
418 && !result
&& timeout_is_event
)
419 evl
->result_flags
|= _NC_EVENT_TIMEOUT_MSEC
;
424 #endif /* USE_FUNC_POLL, etc */
426 returntime
= _nc_gettime(&t0
, FALSE
);
428 if (milliseconds
>= 0)
429 milliseconds
-= (int) (returntime
- starttime
);
431 #ifdef NCURSES_WGETCH_EVENTS
433 evl
->result_flags
= 0;
434 for (n
= 0; n
< evl
->count
; ++n
) {
435 _nc_event
*ev
= evl
->events
[n
];
437 if (ev
->type
== _NC_EVENT_TIMEOUT_MSEC
) {
438 long diff
= (returntime
- starttime
);
439 if (ev
->data
.timeout_msec
<= diff
)
440 ev
->data
.timeout_msec
= 0;
442 ev
->data
.timeout_msec
-= diff
;
449 #if PRECISE_GETTIME && HAVE_NANOSLEEP
451 * If the timeout hasn't expired, and we've gotten no data,
452 * this is probably a system where 'select()' needs to be left
453 * alone so that it can complete. Make this process sleep,
454 * then come back for more.
456 if (result
== 0 && milliseconds
> 100) {
457 napms(100); /* FIXME: this won't be right if I recur! */
463 /* return approximate time left in milliseconds */
465 *timeleft
= milliseconds
;
467 TR(TRACE_IEVENT
, ("end twait: returned %d (%d), remaining time %d msec",
468 result
, errno
, milliseconds
));
471 * Both 'poll()' and 'select()' return the number of file descriptors
472 * that are active. Translate this back to the mask that denotes which
473 * file-descriptors, so that we don't need all of this system-specific
480 for (count
= 0; count
< MIN_FDS
; count
++) {
481 if ((mode
& (1 << count
))
482 && (fds
[count
].revents
& POLLIN
)) {
483 result
|= (1 << count
);
486 #elif defined(__BEOS__)
487 result
= TW_INPUT
; /* redundant, but simple */
489 if ((mode
& TW_MOUSE
)
490 && (fd
= sp
->_mouse_fd
) >= 0
491 && FD_ISSET(fd
, &set
))
493 if ((mode
& TW_INPUT
)
494 && FD_ISSET(sp
->_ifd
, &set
))
500 #ifdef NCURSES_WGETCH_EVENTS
501 if ((mode
& TW_EVENT
) && evl
&& evl
->result_flags
)
506 #ifdef NCURSES_WGETCH_EVENTS