150276Speter/**************************************************************************** 2262685Sdelphij * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc. * 350276Speter * * 450276Speter * Permission is hereby granted, free of charge, to any person obtaining a * 550276Speter * copy of this software and associated documentation files (the * 650276Speter * "Software"), to deal in the Software without restriction, including * 750276Speter * without limitation the rights to use, copy, modify, merge, publish, * 850276Speter * distribute, distribute with modifications, sublicense, and/or sell * 950276Speter * copies of the Software, and to permit persons to whom the Software is * 1050276Speter * furnished to do so, subject to the following conditions: * 1150276Speter * * 1250276Speter * The above copyright notice and this permission notice shall be included * 1350276Speter * in all copies or substantial portions of the Software. * 1450276Speter * * 1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 2250276Speter * * 2350276Speter * Except as contained in this notice, the name(s) of the above copyright * 2450276Speter * holders shall not be used in advertising or otherwise to promote the * 2550276Speter * sale, use or other dealings in this Software without prior written * 2650276Speter * authorization. * 2750276Speter ****************************************************************************/ 2850276Speter 2950276Speter/**************************************************************************** 3050276Speter * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 3150276Speter * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32174993Srafan * and: Thomas E. Dickey 1996-on * 3350276Speter ****************************************************************************/ 3450276Speter 3550276Speter/* 3650276Speter** lib_twait.c 3750276Speter** 3850276Speter** The routine _nc_timed_wait(). 3950276Speter** 4050276Speter** (This file was originally written by Eric Raymond; however except for 4150276Speter** comments, none of the original code remains - T.Dickey). 4250276Speter*/ 4350276Speter 4497049Speter#include <curses.priv.h> 4597049Speter 46184989Srafan#if defined __HAIKU__ && defined __BEOS__ 47184989Srafan#undef __BEOS__ 48184989Srafan#endif 49184989Srafan 5056639Speter#ifdef __BEOS__ 5197049Speter#undef false 5297049Speter#undef true 5356639Speter#include <OS.h> 5456639Speter#endif 5556639Speter 56262685Sdelphij#if USE_KLIBC_KBD 57262685Sdelphij#define INCL_KBD 58262685Sdelphij#include <os2.h> 59262685Sdelphij#endif 60262685Sdelphij 6150276Speter#if USE_FUNC_POLL 6250276Speter# if HAVE_SYS_TIME_H 6350276Speter# include <sys/time.h> 6450276Speter# endif 6550276Speter#elif HAVE_SELECT 6650276Speter# if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT 6750276Speter# include <sys/time.h> 6850276Speter# endif 6950276Speter# if HAVE_SYS_SELECT_H 7050276Speter# include <sys/select.h> 7150276Speter# endif 7250276Speter#endif 73262629Sdelphij#ifdef __MINGW32__ 74262629Sdelphij# include <sys/time.h> 75262629Sdelphij#endif 76184989Srafan#undef CUR 7750276Speter 78262685SdelphijMODULE_ID("$Id: lib_twait.c,v 1.67 2013/02/18 09:22:27 tom Exp $") 79184989Srafan 8066963Speterstatic long 81262685Sdelphij_nc_gettime(TimeType * t0, int first) 8250276Speter{ 8366963Speter long res; 8450276Speter 85174993Srafan#if PRECISE_GETTIME 86174993Srafan TimeType t1; 8766963Speter gettimeofday(&t1, (struct timezone *) 0); 8866963Speter if (first) { 89174993Srafan *t0 = t1; 90166124Srafan res = 0; 91166124Srafan } else { 92166124Srafan /* .tv_sec and .tv_usec are unsigned, be careful when subtracting */ 93174993Srafan if (t0->tv_usec > t1.tv_usec) { 94174993Srafan t1.tv_usec += 1000000; /* Convert 1s in 1e6 microsecs */ 95166124Srafan t1.tv_sec--; 96166124Srafan } 97174993Srafan res = (t1.tv_sec - t0->tv_sec) * 1000 98174993Srafan + (t1.tv_usec - t0->tv_usec) / 1000; 9966963Speter } 10050276Speter#else 10166963Speter time_t t1 = time((time_t *) 0); 10266963Speter if (first) { 103174993Srafan *t0 = t1; 10466963Speter } 105174993Srafan res = (t1 - *t0) * 1000; 10650276Speter#endif 10797049Speter TR(TRACE_IEVENT, ("%s time: %ld msec", first ? "get" : "elapsed", res)); 10866963Speter return res; 10950276Speter} 11050276Speter 111166124Srafan#ifdef NCURSES_WGETCH_EVENTS 112166124SrafanNCURSES_EXPORT(int) 113166124Srafan_nc_eventlist_timeout(_nc_eventlist * evl) 114166124Srafan{ 115166124Srafan int event_delay = -1; 116166124Srafan int n; 117166124Srafan 118166124Srafan if (evl != 0) { 119166124Srafan 120166124Srafan for (n = 0; n < evl->count; ++n) { 121166124Srafan _nc_event *ev = evl->events[n]; 122166124Srafan 123166124Srafan if (ev->type == _NC_EVENT_TIMEOUT_MSEC) { 124166124Srafan event_delay = ev->data.timeout_msec; 125166124Srafan if (event_delay < 0) 126166124Srafan event_delay = INT_MAX; /* FIXME Is this defined? */ 127166124Srafan } 128166124Srafan } 129166124Srafan } 130166124Srafan return event_delay; 131166124Srafan} 132166124Srafan#endif /* NCURSES_WGETCH_EVENTS */ 133166124Srafan 134262629Sdelphij#if (USE_FUNC_POLL || HAVE_SELECT) 135262629Sdelphij# define MAYBE_UNUSED 136262629Sdelphij#else 137262629Sdelphij# define MAYBE_UNUSED GCC_UNUSED 138262629Sdelphij#endif 139262629Sdelphij 140262629Sdelphij#if (USE_FUNC_POLL || HAVE_SELECT) 141262629Sdelphij# define MAYBE_UNUSED 142262629Sdelphij#else 143262629Sdelphij# define MAYBE_UNUSED GCC_UNUSED 144262629Sdelphij#endif 145262629Sdelphij 14650276Speter/* 14750276Speter * Wait a specified number of milliseconds, returning nonzero if the timer 14850276Speter * didn't expire before there is activity on the specified file descriptors. 14950276Speter * The file-descriptors are specified by the mode: 150262629Sdelphij * TW_NONE 0 - none (absolute time) 151262629Sdelphij * TW_INPUT 1 - ncurses' normal input-descriptor 152262629Sdelphij * TW_MOUSE 2 - mouse descriptor, if any 153262629Sdelphij * TW_ANY 3 - either input or mouse. 154262629Sdelphij * TW_EVENT 4 - 155166124Srafan * Experimental: if NCURSES_WGETCH_EVENTS is defined, (mode & 4) determines 156166124Srafan * whether to pay attention to evl argument. If set, the smallest of 157166124Srafan * millisecond and of timeout of evl is taken. 158166124Srafan * 15950276Speter * We return a mask that corresponds to the mode (e.g., 2 for mouse activity). 16050276Speter * 16150276Speter * If the milliseconds given are -1, the wait blocks until activity on the 16250276Speter * descriptors. 16350276Speter */ 16476726SpeterNCURSES_EXPORT(int) 165262629Sdelphij_nc_timed_wait(SCREEN *sp MAYBE_UNUSED, 166262629Sdelphij int mode MAYBE_UNUSED, 167166124Srafan int milliseconds, 168166124Srafan int *timeleft 169166124Srafan EVENTLIST_2nd(_nc_eventlist * evl)) 17050276Speter{ 17166963Speter int count; 172262629Sdelphij int result = TW_NONE; 173174993Srafan TimeType t0; 174262629Sdelphij#if (USE_FUNC_POLL || HAVE_SELECT) 175262629Sdelphij int fd; 176262629Sdelphij#endif 17750276Speter 178166124Srafan#ifdef NCURSES_WGETCH_EVENTS 179166124Srafan int timeout_is_event = 0; 180166124Srafan int n; 181166124Srafan#endif 182166124Srafan 18350276Speter#if USE_FUNC_POLL 184166124Srafan#define MIN_FDS 2 185166124Srafan struct pollfd fd_list[MIN_FDS]; 186166124Srafan struct pollfd *fds = fd_list; 18756639Speter#elif defined(__BEOS__) 18850276Speter#elif HAVE_SELECT 189174993Srafan fd_set set; 19050276Speter#endif 19150276Speter 192262685Sdelphij#if USE_KLIBC_KBD 193262685Sdelphij fd_set saved_set; 194262685Sdelphij KBDKEYINFO ki; 195262685Sdelphij struct timeval tv; 196262685Sdelphij#endif 197262685Sdelphij 19866963Speter long starttime, returntime; 19950276Speter 20097049Speter TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d", 20197049Speter milliseconds, mode)); 20250276Speter 203166124Srafan#ifdef NCURSES_WGETCH_EVENTS 204262629Sdelphij if (mode & TW_EVENT) { 205166124Srafan int event_delay = _nc_eventlist_timeout(evl); 206166124Srafan 207166124Srafan if (event_delay >= 0 208166124Srafan && (milliseconds >= event_delay || milliseconds < 0)) { 209166124Srafan milliseconds = event_delay; 210166124Srafan timeout_is_event = 1; 211166124Srafan } 212166124Srafan } 213166124Srafan#endif 214166124Srafan 215174993Srafan#if PRECISE_GETTIME && HAVE_NANOSLEEP 21666963Speter retry: 21750276Speter#endif 218174993Srafan starttime = _nc_gettime(&t0, TRUE); 21950276Speter 22066963Speter count = 0; 221262685Sdelphij (void) count; 22250276Speter 223166124Srafan#ifdef NCURSES_WGETCH_EVENTS 224262629Sdelphij if ((mode & TW_EVENT) && evl) 225166124Srafan evl->result_flags = 0; 226166124Srafan#endif 227166124Srafan 22850276Speter#if USE_FUNC_POLL 229166124Srafan memset(fd_list, 0, sizeof(fd_list)); 230166124Srafan 231166124Srafan#ifdef NCURSES_WGETCH_EVENTS 232262685Sdelphij if ((mode & TW_EVENT) && evl) { 233166124Srafan fds = typeMalloc(struct pollfd, MIN_FDS + evl->count); 234262685Sdelphij if (fds == 0) 235262685Sdelphij return TW_NONE; 236262685Sdelphij } 237166124Srafan#endif 238166124Srafan 239262629Sdelphij if (mode & TW_INPUT) { 240178866Srafan fds[count].fd = sp->_ifd; 24166963Speter fds[count].events = POLLIN; 24266963Speter count++; 24366963Speter } 244262629Sdelphij if ((mode & TW_MOUSE) 245178866Srafan && (fd = sp->_mouse_fd) >= 0) { 24666963Speter fds[count].fd = fd; 24766963Speter fds[count].events = POLLIN; 24866963Speter count++; 24966963Speter } 250166124Srafan#ifdef NCURSES_WGETCH_EVENTS 251262629Sdelphij if ((mode & TW_EVENT) && evl) { 252166124Srafan for (n = 0; n < evl->count; ++n) { 253166124Srafan _nc_event *ev = evl->events[n]; 25450276Speter 255166124Srafan if (ev->type == _NC_EVENT_FILE 256166124Srafan && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 257166124Srafan fds[count].fd = ev->data.fev.fd; 258166124Srafan fds[count].events = POLLIN; 259166124Srafan count++; 260166124Srafan } 261166124Srafan } 262166124Srafan } 263166124Srafan#endif 264166124Srafan 265262685Sdelphij result = poll(fds, (size_t) count, milliseconds); 266166124Srafan 267166124Srafan#ifdef NCURSES_WGETCH_EVENTS 268262629Sdelphij if ((mode & TW_EVENT) && evl) { 269166124Srafan int c; 270166124Srafan 271166124Srafan if (!result) 272166124Srafan count = 0; 273166124Srafan 274166124Srafan for (n = 0; n < evl->count; ++n) { 275166124Srafan _nc_event *ev = evl->events[n]; 276166124Srafan 277166124Srafan if (ev->type == _NC_EVENT_FILE 278166124Srafan && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 279166124Srafan ev->data.fev.result = 0; 280166124Srafan for (c = 0; c < count; c++) 281166124Srafan if (fds[c].fd == ev->data.fev.fd 282166124Srafan && fds[c].revents & POLLIN) { 283166124Srafan ev->data.fev.result |= _NC_EVENT_FILE_READABLE; 284166124Srafan evl->result_flags |= _NC_EVENT_FILE_READABLE; 285166124Srafan } 286166124Srafan } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC 287166124Srafan && !result && timeout_is_event) { 288166124Srafan evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC; 289166124Srafan } 290166124Srafan } 291166124Srafan } 292166124Srafan#endif 293166124Srafan 29456639Speter#elif defined(__BEOS__) 29566963Speter /* 29666963Speter * BeOS's select() is declared in socket.h, so the configure script does 29766963Speter * not see it. That's just as well, since that function works only for 29866963Speter * sockets. This (using snooze and ioctl) was distilled from Be's patch 29966963Speter * for ncurses which uses a separate thread to simulate select(). 30066963Speter * 30166963Speter * FIXME: the return values from the ioctl aren't very clear if we get 30266963Speter * interrupted. 303166124Srafan * 304166124Srafan * FIXME: this assumes mode&1 if milliseconds < 0 (see lib_getch.c). 30566963Speter */ 306262629Sdelphij result = TW_NONE; 307262629Sdelphij if (mode & TW_INPUT) { 308166124Srafan int step = (milliseconds < 0) ? 0 : 5000; 30966963Speter bigtime_t d; 31066963Speter bigtime_t useconds = milliseconds * 1000; 31166963Speter int n, howmany; 31256639Speter 313166124Srafan if (useconds <= 0) /* we're here to go _through_ the loop */ 31466963Speter useconds = 1; 31556639Speter 316166124Srafan for (d = 0; d < useconds; d += step) { 31766963Speter n = 0; 31866963Speter howmany = ioctl(0, 'ichr', &n); 31966963Speter if (howmany >= 0 && n > 0) { 32066963Speter result = 1; 32166963Speter break; 32266963Speter } 323166124Srafan if (useconds > 1 && step > 0) { 324166124Srafan snooze(step); 325166124Srafan milliseconds -= (step / 1000); 326166124Srafan if (milliseconds <= 0) { 327166124Srafan milliseconds = 0; 328166124Srafan break; 329166124Srafan } 330166124Srafan } 33156639Speter } 33266963Speter } else if (milliseconds > 0) { 33366963Speter snooze(milliseconds * 1000); 33466963Speter milliseconds = 0; 33566963Speter } 33650276Speter#elif HAVE_SELECT 33766963Speter /* 33866963Speter * select() modifies the fd_set arguments; do this in the 33966963Speter * loop. 34066963Speter */ 34166963Speter FD_ZERO(&set); 34250276Speter 343262685Sdelphij#if !USE_KLIBC_KBD 344262629Sdelphij if (mode & TW_INPUT) { 345178866Srafan FD_SET(sp->_ifd, &set); 346178866Srafan count = sp->_ifd + 1; 34766963Speter } 348262685Sdelphij#endif 349262629Sdelphij if ((mode & TW_MOUSE) 350178866Srafan && (fd = sp->_mouse_fd) >= 0) { 35166963Speter FD_SET(fd, &set); 35266963Speter count = max(fd, count) + 1; 35366963Speter } 354166124Srafan#ifdef NCURSES_WGETCH_EVENTS 355262629Sdelphij if ((mode & TW_EVENT) && evl) { 356166124Srafan for (n = 0; n < evl->count; ++n) { 357166124Srafan _nc_event *ev = evl->events[n]; 35850276Speter 359166124Srafan if (ev->type == _NC_EVENT_FILE 360166124Srafan && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 361166124Srafan FD_SET(ev->data.fev.fd, &set); 362166124Srafan count = max(ev->data.fev.fd + 1, count); 363166124Srafan } 364166124Srafan } 365166124Srafan } 366166124Srafan#endif 367166124Srafan 368262685Sdelphij#if USE_KLIBC_KBD 369262685Sdelphij for (saved_set = set;; set = saved_set) { 370262685Sdelphij if ((mode & TW_INPUT) 371262685Sdelphij && (sp->_extended_key 372262685Sdelphij || (KbdPeek(&ki, 0) == 0 373262685Sdelphij && (ki.fbStatus & KBDTRF_FINAL_CHAR_IN)))) { 374262685Sdelphij FD_ZERO(&set); 375262685Sdelphij FD_SET(sp->_ifd, &set); 376262685Sdelphij result = 1; 377262685Sdelphij break; 378262685Sdelphij } 379262685Sdelphij 380262685Sdelphij tv.tv_sec = 0; 381262685Sdelphij tv.tv_usec = (milliseconds == 0) ? 0 : (10 * 1000); 382262685Sdelphij 383262685Sdelphij if ((result = select(count, &set, NULL, NULL, &tv)) != 0) 384262685Sdelphij break; 385262685Sdelphij 386262685Sdelphij /* Time out ? */ 387262685Sdelphij if (milliseconds >= 0 && _nc_gettime(&t0, FALSE) >= milliseconds) { 388262685Sdelphij result = 0; 389262685Sdelphij break; 390262685Sdelphij } 391262685Sdelphij } 392262685Sdelphij#else 39366963Speter if (milliseconds >= 0) { 39466963Speter struct timeval ntimeout; 39566963Speter ntimeout.tv_sec = milliseconds / 1000; 39666963Speter ntimeout.tv_usec = (milliseconds % 1000) * 1000; 39766963Speter result = select(count, &set, NULL, NULL, &ntimeout); 39866963Speter } else { 39966963Speter result = select(count, &set, NULL, NULL, NULL); 40066963Speter } 401262685Sdelphij#endif 402166124Srafan 403166124Srafan#ifdef NCURSES_WGETCH_EVENTS 404262629Sdelphij if ((mode & TW_EVENT) && evl) { 405166124Srafan evl->result_flags = 0; 406166124Srafan for (n = 0; n < evl->count; ++n) { 407166124Srafan _nc_event *ev = evl->events[n]; 408166124Srafan 409166124Srafan if (ev->type == _NC_EVENT_FILE 410166124Srafan && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 411166124Srafan ev->data.fev.result = 0; 412166124Srafan if (FD_ISSET(ev->data.fev.fd, &set)) { 413166124Srafan ev->data.fev.result |= _NC_EVENT_FILE_READABLE; 414166124Srafan evl->result_flags |= _NC_EVENT_FILE_READABLE; 415166124Srafan } 416166124Srafan } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC 417166124Srafan && !result && timeout_is_event) 418166124Srafan evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC; 419166124Srafan } 420166124Srafan } 42150276Speter#endif 42250276Speter 423166124Srafan#endif /* USE_FUNC_POLL, etc */ 424166124Srafan 425174993Srafan returntime = _nc_gettime(&t0, FALSE); 42650276Speter 42766963Speter if (milliseconds >= 0) 428262629Sdelphij milliseconds -= (int) (returntime - starttime); 42950276Speter 430166124Srafan#ifdef NCURSES_WGETCH_EVENTS 431166124Srafan if (evl) { 432166124Srafan evl->result_flags = 0; 433166124Srafan for (n = 0; n < evl->count; ++n) { 434166124Srafan _nc_event *ev = evl->events[n]; 435166124Srafan 436166124Srafan if (ev->type == _NC_EVENT_TIMEOUT_MSEC) { 437166124Srafan long diff = (returntime - starttime); 438166124Srafan if (ev->data.timeout_msec <= diff) 439166124Srafan ev->data.timeout_msec = 0; 440166124Srafan else 441166124Srafan ev->data.timeout_msec -= diff; 442166124Srafan } 443166124Srafan 444166124Srafan } 445166124Srafan } 446166124Srafan#endif 447166124Srafan 448166124Srafan#if PRECISE_GETTIME && HAVE_NANOSLEEP 44966963Speter /* 45066963Speter * If the timeout hasn't expired, and we've gotten no data, 45166963Speter * this is probably a system where 'select()' needs to be left 45266963Speter * alone so that it can complete. Make this process sleep, 45366963Speter * then come back for more. 45466963Speter */ 45566963Speter if (result == 0 && milliseconds > 100) { 456166124Srafan napms(100); /* FIXME: this won't be right if I recur! */ 45766963Speter milliseconds -= 100; 45866963Speter goto retry; 45966963Speter } 46050276Speter#endif 46150276Speter 46266963Speter /* return approximate time left in milliseconds */ 46366963Speter if (timeleft) 46466963Speter *timeleft = milliseconds; 46550276Speter 46697049Speter TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec", 46797049Speter result, errno, milliseconds)); 46850276Speter 46966963Speter /* 47066963Speter * Both 'poll()' and 'select()' return the number of file descriptors 47166963Speter * that are active. Translate this back to the mask that denotes which 47266963Speter * file-descriptors, so that we don't need all of this system-specific 47366963Speter * code everywhere. 47466963Speter */ 47566963Speter if (result != 0) { 47666963Speter if (result > 0) { 47766963Speter result = 0; 47850276Speter#if USE_FUNC_POLL 479166124Srafan for (count = 0; count < MIN_FDS; count++) { 48066963Speter if ((mode & (1 << count)) 48166963Speter && (fds[count].revents & POLLIN)) { 48266963Speter result |= (1 << count); 48366963Speter } 48466963Speter } 48556639Speter#elif defined(__BEOS__) 486262629Sdelphij result = TW_INPUT; /* redundant, but simple */ 48750276Speter#elif HAVE_SELECT 488262629Sdelphij if ((mode & TW_MOUSE) 489178866Srafan && (fd = sp->_mouse_fd) >= 0 49066963Speter && FD_ISSET(fd, &set)) 491262629Sdelphij result |= TW_MOUSE; 492262629Sdelphij if ((mode & TW_INPUT) 493178866Srafan && FD_ISSET(sp->_ifd, &set)) 494262629Sdelphij result |= TW_INPUT; 49550276Speter#endif 49666963Speter } else 49766963Speter result = 0; 49866963Speter } 499166124Srafan#ifdef NCURSES_WGETCH_EVENTS 500262629Sdelphij if ((mode & TW_EVENT) && evl && evl->result_flags) 501262629Sdelphij result |= TW_EVENT; 502166124Srafan#endif 50350276Speter 504262685Sdelphij#if USE_FUNC_POLL 505262685Sdelphij#ifdef NCURSES_WGETCH_EVENTS 506262685Sdelphij if (fds != fd_list) 507262685Sdelphij free((char *) fds); 508262685Sdelphij#endif 509262685Sdelphij#endif 510262685Sdelphij 51166963Speter return (result); 51250276Speter} 513