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