1 /* $OpenBSD: lib_twait.c,v 1.9 2010/01/12 23:22:07 nicm Exp $ */ 2 3 /**************************************************************************** 4 * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. * 5 * * 6 * Permission is hereby granted, free of charge, to any person obtaining a * 7 * copy of this software and associated documentation files (the * 8 * "Software"), to deal in the Software without restriction, including * 9 * without limitation the rights to use, copy, modify, merge, publish, * 10 * distribute, distribute with modifications, sublicense, and/or sell * 11 * copies of the Software, and to permit persons to whom the Software is * 12 * furnished to do so, subject to the following conditions: * 13 * * 14 * The above copyright notice and this permission notice shall be included * 15 * in all copies or substantial portions of the Software. * 16 * * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 24 * * 25 * Except as contained in this notice, the name(s) of the above copyright * 26 * holders shall not be used in advertising or otherwise to promote the * 27 * sale, use or other dealings in this Software without prior written * 28 * authorization. * 29 ****************************************************************************/ 30 31 /**************************************************************************** 32 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 33 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 34 * and: Thomas E. Dickey 1996-on * 35 ****************************************************************************/ 36 37 /* 38 ** lib_twait.c 39 ** 40 ** The routine _nc_timed_wait(). 41 ** 42 ** (This file was originally written by Eric Raymond; however except for 43 ** comments, none of the original code remains - T.Dickey). 44 */ 45 46 #include <curses.priv.h> 47 48 #if defined __HAIKU__ && defined __BEOS__ 49 #undef __BEOS__ 50 #endif 51 52 #ifdef __BEOS__ 53 #undef false 54 #undef true 55 #include <OS.h> 56 #endif 57 58 #if USE_FUNC_POLL 59 # if HAVE_SYS_TIME_H 60 # include <sys/time.h> 61 # endif 62 #elif HAVE_SELECT 63 # if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT 64 # include <sys/time.h> 65 # endif 66 # if HAVE_SYS_SELECT_H 67 # include <sys/select.h> 68 # endif 69 #endif 70 71 #undef CUR 72 73 MODULE_ID("$Id: lib_twait.c,v 1.9 2010/01/12 23:22:07 nicm 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 /* 130 * Wait a specified number of milliseconds, returning nonzero if the timer 131 * didn't expire before there is activity on the specified file descriptors. 132 * The file-descriptors are specified by the mode: 133 * 0 - none (absolute time) 134 * 1 - ncurses' normal input-descriptor 135 * 2 - mouse descriptor, if any 136 * 3 - either input or mouse. 137 * 138 * Experimental: if NCURSES_WGETCH_EVENTS is defined, (mode & 4) determines 139 * whether to pay attention to evl argument. If set, the smallest of 140 * millisecond and of timeout of evl is taken. 141 * 142 * We return a mask that corresponds to the mode (e.g., 2 for mouse activity). 143 * 144 * If the milliseconds given are -1, the wait blocks until activity on the 145 * descriptors. 146 */ 147 NCURSES_EXPORT(int) 148 _nc_timed_wait(SCREEN *sp, 149 int mode, 150 int milliseconds, 151 int *timeleft 152 EVENTLIST_2nd(_nc_eventlist * evl)) 153 { 154 int fd; 155 int count; 156 int result = 0; 157 TimeType t0; 158 159 #ifdef NCURSES_WGETCH_EVENTS 160 int timeout_is_event = 0; 161 int n; 162 #endif 163 164 #if USE_FUNC_POLL 165 #define MIN_FDS 2 166 struct pollfd fd_list[MIN_FDS]; 167 struct pollfd *fds = fd_list; 168 #elif defined(__BEOS__) 169 #elif HAVE_SELECT 170 fd_set set; 171 #endif 172 173 long starttime, returntime; 174 175 TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d", 176 milliseconds, mode)); 177 178 #ifdef NCURSES_WGETCH_EVENTS 179 if (mode & 4) { 180 int event_delay = _nc_eventlist_timeout(evl); 181 182 if (event_delay >= 0 183 && (milliseconds >= event_delay || milliseconds < 0)) { 184 milliseconds = event_delay; 185 timeout_is_event = 1; 186 } 187 } 188 #endif 189 190 #if PRECISE_GETTIME && HAVE_NANOSLEEP 191 retry: 192 #endif 193 starttime = _nc_gettime(&t0, TRUE); 194 195 count = 0; 196 197 #ifdef NCURSES_WGETCH_EVENTS 198 if ((mode & 4) && evl) 199 evl->result_flags = 0; 200 #endif 201 202 #if USE_FUNC_POLL 203 memset(fd_list, 0, sizeof(fd_list)); 204 205 #ifdef NCURSES_WGETCH_EVENTS 206 if ((mode & 4) && evl) 207 fds = typeMalloc(struct pollfd, MIN_FDS + evl->count); 208 #endif 209 210 if (mode & 1) { 211 fds[count].fd = sp->_ifd; 212 fds[count].events = POLLIN; 213 count++; 214 } 215 if ((mode & 2) 216 && (fd = sp->_mouse_fd) >= 0) { 217 fds[count].fd = fd; 218 fds[count].events = POLLIN; 219 count++; 220 } 221 #ifdef NCURSES_WGETCH_EVENTS 222 if ((mode & 4) && evl) { 223 for (n = 0; n < evl->count; ++n) { 224 _nc_event *ev = evl->events[n]; 225 226 if (ev->type == _NC_EVENT_FILE 227 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 228 fds[count].fd = ev->data.fev.fd; 229 fds[count].events = POLLIN; 230 count++; 231 } 232 } 233 } 234 #endif 235 236 result = poll(fds, (unsigned) count, milliseconds); 237 238 #ifdef NCURSES_WGETCH_EVENTS 239 if ((mode & 4) && evl) { 240 int c; 241 242 if (!result) 243 count = 0; 244 245 for (n = 0; n < evl->count; ++n) { 246 _nc_event *ev = evl->events[n]; 247 248 if (ev->type == _NC_EVENT_FILE 249 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 250 ev->data.fev.result = 0; 251 for (c = 0; c < count; c++) 252 if (fds[c].fd == ev->data.fev.fd 253 && fds[c].revents & POLLIN) { 254 ev->data.fev.result |= _NC_EVENT_FILE_READABLE; 255 evl->result_flags |= _NC_EVENT_FILE_READABLE; 256 } 257 } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC 258 && !result && timeout_is_event) { 259 evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC; 260 } 261 } 262 } 263 264 if (fds != fd_list) 265 free((char *) fds); 266 267 #endif 268 269 #elif defined(__BEOS__) 270 /* 271 * BeOS's select() is declared in socket.h, so the configure script does 272 * not see it. That's just as well, since that function works only for 273 * sockets. This (using snooze and ioctl) was distilled from Be's patch 274 * for ncurses which uses a separate thread to simulate select(). 275 * 276 * FIXME: the return values from the ioctl aren't very clear if we get 277 * interrupted. 278 * 279 * FIXME: this assumes mode&1 if milliseconds < 0 (see lib_getch.c). 280 */ 281 result = 0; 282 if (mode & 1) { 283 int step = (milliseconds < 0) ? 0 : 5000; 284 bigtime_t d; 285 bigtime_t useconds = milliseconds * 1000; 286 int n, howmany; 287 288 if (useconds <= 0) /* we're here to go _through_ the loop */ 289 useconds = 1; 290 291 for (d = 0; d < useconds; d += step) { 292 n = 0; 293 howmany = ioctl(0, 'ichr', &n); 294 if (howmany >= 0 && n > 0) { 295 result = 1; 296 break; 297 } 298 if (useconds > 1 && step > 0) { 299 snooze(step); 300 milliseconds -= (step / 1000); 301 if (milliseconds <= 0) { 302 milliseconds = 0; 303 break; 304 } 305 } 306 } 307 } else if (milliseconds > 0) { 308 snooze(milliseconds * 1000); 309 milliseconds = 0; 310 } 311 #elif HAVE_SELECT 312 /* 313 * select() modifies the fd_set arguments; do this in the 314 * loop. 315 */ 316 FD_ZERO(&set); 317 318 if (mode & 1) { 319 FD_SET(sp->_ifd, &set); 320 count = sp->_ifd + 1; 321 } 322 if ((mode & 2) 323 && (fd = sp->_mouse_fd) >= 0) { 324 FD_SET(fd, &set); 325 count = max(fd, count) + 1; 326 } 327 #ifdef NCURSES_WGETCH_EVENTS 328 if ((mode & 4) && evl) { 329 for (n = 0; n < evl->count; ++n) { 330 _nc_event *ev = evl->events[n]; 331 332 if (ev->type == _NC_EVENT_FILE 333 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 334 FD_SET(ev->data.fev.fd, &set); 335 count = max(ev->data.fev.fd + 1, count); 336 } 337 } 338 } 339 #endif 340 341 if (milliseconds >= 0) { 342 struct timeval ntimeout; 343 ntimeout.tv_sec = milliseconds / 1000; 344 ntimeout.tv_usec = (milliseconds % 1000) * 1000; 345 result = select(count, &set, NULL, NULL, &ntimeout); 346 } else { 347 result = select(count, &set, NULL, NULL, NULL); 348 } 349 350 #ifdef NCURSES_WGETCH_EVENTS 351 if ((mode & 4) && evl) { 352 evl->result_flags = 0; 353 for (n = 0; n < evl->count; ++n) { 354 _nc_event *ev = evl->events[n]; 355 356 if (ev->type == _NC_EVENT_FILE 357 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 358 ev->data.fev.result = 0; 359 if (FD_ISSET(ev->data.fev.fd, &set)) { 360 ev->data.fev.result |= _NC_EVENT_FILE_READABLE; 361 evl->result_flags |= _NC_EVENT_FILE_READABLE; 362 } 363 } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC 364 && !result && timeout_is_event) 365 evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC; 366 } 367 } 368 #endif 369 370 #endif /* USE_FUNC_POLL, etc */ 371 372 returntime = _nc_gettime(&t0, FALSE); 373 374 if (milliseconds >= 0) 375 milliseconds -= (returntime - starttime); 376 377 #ifdef NCURSES_WGETCH_EVENTS 378 if (evl) { 379 evl->result_flags = 0; 380 for (n = 0; n < evl->count; ++n) { 381 _nc_event *ev = evl->events[n]; 382 383 if (ev->type == _NC_EVENT_TIMEOUT_MSEC) { 384 long diff = (returntime - starttime); 385 if (ev->data.timeout_msec <= diff) 386 ev->data.timeout_msec = 0; 387 else 388 ev->data.timeout_msec -= diff; 389 } 390 391 } 392 } 393 #endif 394 395 #if PRECISE_GETTIME && HAVE_NANOSLEEP 396 /* 397 * If the timeout hasn't expired, and we've gotten no data, 398 * this is probably a system where 'select()' needs to be left 399 * alone so that it can complete. Make this process sleep, 400 * then come back for more. 401 */ 402 if (result == 0 && milliseconds > 100) { 403 napms(100); /* FIXME: this won't be right if I recur! */ 404 milliseconds -= 100; 405 goto retry; 406 } 407 #endif 408 409 /* return approximate time left in milliseconds */ 410 if (timeleft) 411 *timeleft = milliseconds; 412 413 TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec", 414 result, errno, milliseconds)); 415 416 /* 417 * Both 'poll()' and 'select()' return the number of file descriptors 418 * that are active. Translate this back to the mask that denotes which 419 * file-descriptors, so that we don't need all of this system-specific 420 * code everywhere. 421 */ 422 if (result != 0) { 423 if (result > 0) { 424 result = 0; 425 #if USE_FUNC_POLL 426 for (count = 0; count < MIN_FDS; count++) { 427 if ((mode & (1 << count)) 428 && (fds[count].revents & POLLIN)) { 429 result |= (1 << count); 430 } 431 } 432 #elif defined(__BEOS__) 433 result = 1; /* redundant, but simple */ 434 #elif HAVE_SELECT 435 if ((mode & 2) 436 && (fd = sp->_mouse_fd) >= 0 437 && FD_ISSET(fd, &set)) 438 result |= 2; 439 if ((mode & 1) 440 && FD_ISSET(sp->_ifd, &set)) 441 result |= 1; 442 #endif 443 } else 444 result = 0; 445 } 446 #ifdef NCURSES_WGETCH_EVENTS 447 if ((mode & 4) && evl && evl->result_flags) 448 result |= 4; 449 #endif 450 451 return (result); 452 } 453