lib_twait.c revision 302408
1/**************************************************************************** 2 * Copyright (c) 1998-2012,2013 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_KLIBC_KBD 57#define INCL_KBD 58#include <os2.h> 59#endif 60 61#if USE_FUNC_POLL 62# if HAVE_SYS_TIME_H 63# include <sys/time.h> 64# endif 65#elif HAVE_SELECT 66# if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT 67# include <sys/time.h> 68# endif 69# if HAVE_SYS_SELECT_H 70# include <sys/select.h> 71# endif 72#endif 73#ifdef __MINGW32__ 74# include <sys/time.h> 75#endif 76#undef CUR 77 78MODULE_ID("$Id: lib_twait.c,v 1.67 2013/02/18 09:22:27 tom Exp $") 79 80static long 81_nc_gettime(TimeType * t0, int first) 82{ 83 long res; 84 85#if PRECISE_GETTIME 86 TimeType t1; 87 gettimeofday(&t1, (struct timezone *) 0); 88 if (first) { 89 *t0 = t1; 90 res = 0; 91 } else { 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 */ 95 t1.tv_sec--; 96 } 97 res = (t1.tv_sec - t0->tv_sec) * 1000 98 + (t1.tv_usec - t0->tv_usec) / 1000; 99 } 100#else 101 time_t t1 = time((time_t *) 0); 102 if (first) { 103 *t0 = t1; 104 } 105 res = (t1 - *t0) * 1000; 106#endif 107 TR(TRACE_IEVENT, ("%s time: %ld msec", first ? "get" : "elapsed", res)); 108 return res; 109} 110 111#ifdef NCURSES_WGETCH_EVENTS 112NCURSES_EXPORT(int) 113_nc_eventlist_timeout(_nc_eventlist * evl) 114{ 115 int event_delay = -1; 116 int n; 117 118 if (evl != 0) { 119 120 for (n = 0; n < evl->count; ++n) { 121 _nc_event *ev = evl->events[n]; 122 123 if (ev->type == _NC_EVENT_TIMEOUT_MSEC) { 124 event_delay = ev->data.timeout_msec; 125 if (event_delay < 0) 126 event_delay = INT_MAX; /* FIXME Is this defined? */ 127 } 128 } 129 } 130 return event_delay; 131} 132#endif /* NCURSES_WGETCH_EVENTS */ 133 134#if (USE_FUNC_POLL || HAVE_SELECT) 135# define MAYBE_UNUSED 136#else 137# define MAYBE_UNUSED GCC_UNUSED 138#endif 139 140#if (USE_FUNC_POLL || HAVE_SELECT) 141# define MAYBE_UNUSED 142#else 143# define MAYBE_UNUSED GCC_UNUSED 144#endif 145 146/* 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. 154 * TW_EVENT 4 - 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. 158 * 159 * We return a mask that corresponds to the mode (e.g., 2 for mouse activity). 160 * 161 * If the milliseconds given are -1, the wait blocks until activity on the 162 * descriptors. 163 */ 164NCURSES_EXPORT(int) 165_nc_timed_wait(SCREEN *sp MAYBE_UNUSED, 166 int mode MAYBE_UNUSED, 167 int milliseconds, 168 int *timeleft 169 EVENTLIST_2nd(_nc_eventlist * evl)) 170{ 171 int count; 172 int result = TW_NONE; 173 TimeType t0; 174#if (USE_FUNC_POLL || HAVE_SELECT) 175 int fd; 176#endif 177 178#ifdef NCURSES_WGETCH_EVENTS 179 int timeout_is_event = 0; 180 int n; 181#endif 182 183#if USE_FUNC_POLL 184#define MIN_FDS 2 185 struct pollfd fd_list[MIN_FDS]; 186 struct pollfd *fds = fd_list; 187#elif defined(__BEOS__) 188#elif HAVE_SELECT 189 fd_set set; 190#endif 191 192#if USE_KLIBC_KBD 193 fd_set saved_set; 194 KBDKEYINFO ki; 195 struct timeval tv; 196#endif 197 198 long starttime, returntime; 199 200 TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d", 201 milliseconds, mode)); 202 203#ifdef NCURSES_WGETCH_EVENTS 204 if (mode & TW_EVENT) { 205 int event_delay = _nc_eventlist_timeout(evl); 206 207 if (event_delay >= 0 208 && (milliseconds >= event_delay || milliseconds < 0)) { 209 milliseconds = event_delay; 210 timeout_is_event = 1; 211 } 212 } 213#endif 214 215#if PRECISE_GETTIME && HAVE_NANOSLEEP 216 retry: 217#endif 218 starttime = _nc_gettime(&t0, TRUE); 219 220 count = 0; 221 (void) count; 222 223#ifdef NCURSES_WGETCH_EVENTS 224 if ((mode & TW_EVENT) && evl) 225 evl->result_flags = 0; 226#endif 227 228#if USE_FUNC_POLL 229 memset(fd_list, 0, sizeof(fd_list)); 230 231#ifdef NCURSES_WGETCH_EVENTS 232 if ((mode & TW_EVENT) && evl) { 233 fds = typeMalloc(struct pollfd, MIN_FDS + evl->count); 234 if (fds == 0) 235 return TW_NONE; 236 } 237#endif 238 239 if (mode & TW_INPUT) { 240 fds[count].fd = sp->_ifd; 241 fds[count].events = POLLIN; 242 count++; 243 } 244 if ((mode & TW_MOUSE) 245 && (fd = sp->_mouse_fd) >= 0) { 246 fds[count].fd = fd; 247 fds[count].events = POLLIN; 248 count++; 249 } 250#ifdef NCURSES_WGETCH_EVENTS 251 if ((mode & TW_EVENT) && evl) { 252 for (n = 0; n < evl->count; ++n) { 253 _nc_event *ev = evl->events[n]; 254 255 if (ev->type == _NC_EVENT_FILE 256 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 257 fds[count].fd = ev->data.fev.fd; 258 fds[count].events = POLLIN; 259 count++; 260 } 261 } 262 } 263#endif 264 265 result = poll(fds, (size_t) count, milliseconds); 266 267#ifdef NCURSES_WGETCH_EVENTS 268 if ((mode & TW_EVENT) && evl) { 269 int c; 270 271 if (!result) 272 count = 0; 273 274 for (n = 0; n < evl->count; ++n) { 275 _nc_event *ev = evl->events[n]; 276 277 if (ev->type == _NC_EVENT_FILE 278 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 279 ev->data.fev.result = 0; 280 for (c = 0; c < count; c++) 281 if (fds[c].fd == ev->data.fev.fd 282 && fds[c].revents & POLLIN) { 283 ev->data.fev.result |= _NC_EVENT_FILE_READABLE; 284 evl->result_flags |= _NC_EVENT_FILE_READABLE; 285 } 286 } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC 287 && !result && timeout_is_event) { 288 evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC; 289 } 290 } 291 } 292#endif 293 294#elif defined(__BEOS__) 295 /* 296 * BeOS's select() is declared in socket.h, so the configure script does 297 * not see it. That's just as well, since that function works only for 298 * sockets. This (using snooze and ioctl) was distilled from Be's patch 299 * for ncurses which uses a separate thread to simulate select(). 300 * 301 * FIXME: the return values from the ioctl aren't very clear if we get 302 * interrupted. 303 * 304 * FIXME: this assumes mode&1 if milliseconds < 0 (see lib_getch.c). 305 */ 306 result = TW_NONE; 307 if (mode & TW_INPUT) { 308 int step = (milliseconds < 0) ? 0 : 5000; 309 bigtime_t d; 310 bigtime_t useconds = milliseconds * 1000; 311 int n, howmany; 312 313 if (useconds <= 0) /* we're here to go _through_ the loop */ 314 useconds = 1; 315 316 for (d = 0; d < useconds; d += step) { 317 n = 0; 318 howmany = ioctl(0, 'ichr', &n); 319 if (howmany >= 0 && n > 0) { 320 result = 1; 321 break; 322 } 323 if (useconds > 1 && step > 0) { 324 snooze(step); 325 milliseconds -= (step / 1000); 326 if (milliseconds <= 0) { 327 milliseconds = 0; 328 break; 329 } 330 } 331 } 332 } else if (milliseconds > 0) { 333 snooze(milliseconds * 1000); 334 milliseconds = 0; 335 } 336#elif HAVE_SELECT 337 /* 338 * select() modifies the fd_set arguments; do this in the 339 * loop. 340 */ 341 FD_ZERO(&set); 342 343#if !USE_KLIBC_KBD 344 if (mode & TW_INPUT) { 345 FD_SET(sp->_ifd, &set); 346 count = sp->_ifd + 1; 347 } 348#endif 349 if ((mode & TW_MOUSE) 350 && (fd = sp->_mouse_fd) >= 0) { 351 FD_SET(fd, &set); 352 count = max(fd, count) + 1; 353 } 354#ifdef NCURSES_WGETCH_EVENTS 355 if ((mode & TW_EVENT) && evl) { 356 for (n = 0; n < evl->count; ++n) { 357 _nc_event *ev = evl->events[n]; 358 359 if (ev->type == _NC_EVENT_FILE 360 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 361 FD_SET(ev->data.fev.fd, &set); 362 count = max(ev->data.fev.fd + 1, count); 363 } 364 } 365 } 366#endif 367 368#if USE_KLIBC_KBD 369 for (saved_set = set;; set = saved_set) { 370 if ((mode & TW_INPUT) 371 && (sp->_extended_key 372 || (KbdPeek(&ki, 0) == 0 373 && (ki.fbStatus & KBDTRF_FINAL_CHAR_IN)))) { 374 FD_ZERO(&set); 375 FD_SET(sp->_ifd, &set); 376 result = 1; 377 break; 378 } 379 380 tv.tv_sec = 0; 381 tv.tv_usec = (milliseconds == 0) ? 0 : (10 * 1000); 382 383 if ((result = select(count, &set, NULL, NULL, &tv)) != 0) 384 break; 385 386 /* Time out ? */ 387 if (milliseconds >= 0 && _nc_gettime(&t0, FALSE) >= milliseconds) { 388 result = 0; 389 break; 390 } 391 } 392#else 393 if (milliseconds >= 0) { 394 struct timeval ntimeout; 395 ntimeout.tv_sec = milliseconds / 1000; 396 ntimeout.tv_usec = (milliseconds % 1000) * 1000; 397 result = select(count, &set, NULL, NULL, &ntimeout); 398 } else { 399 result = select(count, &set, NULL, NULL, NULL); 400 } 401#endif 402 403#ifdef NCURSES_WGETCH_EVENTS 404 if ((mode & TW_EVENT) && evl) { 405 evl->result_flags = 0; 406 for (n = 0; n < evl->count; ++n) { 407 _nc_event *ev = evl->events[n]; 408 409 if (ev->type == _NC_EVENT_FILE 410 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 411 ev->data.fev.result = 0; 412 if (FD_ISSET(ev->data.fev.fd, &set)) { 413 ev->data.fev.result |= _NC_EVENT_FILE_READABLE; 414 evl->result_flags |= _NC_EVENT_FILE_READABLE; 415 } 416 } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC 417 && !result && timeout_is_event) 418 evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC; 419 } 420 } 421#endif 422 423#endif /* USE_FUNC_POLL, etc */ 424 425 returntime = _nc_gettime(&t0, FALSE); 426 427 if (milliseconds >= 0) 428 milliseconds -= (int) (returntime - starttime); 429 430#ifdef NCURSES_WGETCH_EVENTS 431 if (evl) { 432 evl->result_flags = 0; 433 for (n = 0; n < evl->count; ++n) { 434 _nc_event *ev = evl->events[n]; 435 436 if (ev->type == _NC_EVENT_TIMEOUT_MSEC) { 437 long diff = (returntime - starttime); 438 if (ev->data.timeout_msec <= diff) 439 ev->data.timeout_msec = 0; 440 else 441 ev->data.timeout_msec -= diff; 442 } 443 444 } 445 } 446#endif 447 448#if PRECISE_GETTIME && HAVE_NANOSLEEP 449 /* 450 * If the timeout hasn't expired, and we've gotten no data, 451 * this is probably a system where 'select()' needs to be left 452 * alone so that it can complete. Make this process sleep, 453 * then come back for more. 454 */ 455 if (result == 0 && milliseconds > 100) { 456 napms(100); /* FIXME: this won't be right if I recur! */ 457 milliseconds -= 100; 458 goto retry; 459 } 460#endif 461 462 /* return approximate time left in milliseconds */ 463 if (timeleft) 464 *timeleft = milliseconds; 465 466 TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec", 467 result, errno, milliseconds)); 468 469 /* 470 * Both 'poll()' and 'select()' return the number of file descriptors 471 * that are active. Translate this back to the mask that denotes which 472 * file-descriptors, so that we don't need all of this system-specific 473 * code everywhere. 474 */ 475 if (result != 0) { 476 if (result > 0) { 477 result = 0; 478#if USE_FUNC_POLL 479 for (count = 0; count < MIN_FDS; count++) { 480 if ((mode & (1 << count)) 481 && (fds[count].revents & POLLIN)) { 482 result |= (1 << count); 483 } 484 } 485#elif defined(__BEOS__) 486 result = TW_INPUT; /* redundant, but simple */ 487#elif HAVE_SELECT 488 if ((mode & TW_MOUSE) 489 && (fd = sp->_mouse_fd) >= 0 490 && FD_ISSET(fd, &set)) 491 result |= TW_MOUSE; 492 if ((mode & TW_INPUT) 493 && FD_ISSET(sp->_ifd, &set)) 494 result |= TW_INPUT; 495#endif 496 } else 497 result = 0; 498 } 499#ifdef NCURSES_WGETCH_EVENTS 500 if ((mode & TW_EVENT) && evl && evl->result_flags) 501 result |= TW_EVENT; 502#endif 503 504#if USE_FUNC_POLL 505#ifdef NCURSES_WGETCH_EVENTS 506 if (fds != fd_list) 507 free((char *) fds); 508#endif 509#endif 510 511 return (result); 512} 513