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