1 /**************************************************************************** 2 * Copyright (c) 1998-2014,2015 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 78 MODULE_ID("$Id: lib_twait.c,v 1.70 2015/07/04 21:01:02 tom Exp $") 79 80 static 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 = (long) ((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 112 NCURSES_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 = (int) 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 */ 164 NCURSES_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 if (fds == fd_list) 234 fds = typeMalloc(struct pollfd, MIN_FDS + evl->count); 235 if (fds == 0) 236 return TW_NONE; 237 } 238 #endif 239 240 if (mode & TW_INPUT) { 241 fds[count].fd = sp->_ifd; 242 fds[count].events = POLLIN; 243 count++; 244 } 245 if ((mode & TW_MOUSE) 246 && (fd = sp->_mouse_fd) >= 0) { 247 fds[count].fd = fd; 248 fds[count].events = POLLIN; 249 count++; 250 } 251 #ifdef NCURSES_WGETCH_EVENTS 252 if ((mode & TW_EVENT) && evl) { 253 for (n = 0; n < evl->count; ++n) { 254 _nc_event *ev = evl->events[n]; 255 256 if (ev->type == _NC_EVENT_FILE 257 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 258 fds[count].fd = ev->data.fev.fd; 259 fds[count].events = POLLIN; 260 count++; 261 } 262 } 263 } 264 #endif 265 266 result = poll(fds, (size_t) count, milliseconds); 267 268 #ifdef NCURSES_WGETCH_EVENTS 269 if ((mode & TW_EVENT) && evl) { 270 int c; 271 272 if (!result) 273 count = 0; 274 275 for (n = 0; n < evl->count; ++n) { 276 _nc_event *ev = evl->events[n]; 277 278 if (ev->type == _NC_EVENT_FILE 279 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 280 ev->data.fev.result = 0; 281 for (c = 0; c < count; c++) 282 if (fds[c].fd == ev->data.fev.fd 283 && fds[c].revents & POLLIN) { 284 ev->data.fev.result |= _NC_EVENT_FILE_READABLE; 285 evl->result_flags |= _NC_EVENT_FILE_READABLE; 286 } 287 } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC 288 && !result && timeout_is_event) { 289 evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC; 290 } 291 } 292 } 293 #endif 294 295 #elif defined(__BEOS__) 296 /* 297 * BeOS's select() is declared in socket.h, so the configure script does 298 * not see it. That's just as well, since that function works only for 299 * sockets. This (using snooze and ioctl) was distilled from Be's patch 300 * for ncurses which uses a separate thread to simulate select(). 301 * 302 * FIXME: the return values from the ioctl aren't very clear if we get 303 * interrupted. 304 * 305 * FIXME: this assumes mode&1 if milliseconds < 0 (see lib_getch.c). 306 */ 307 result = TW_NONE; 308 if (mode & TW_INPUT) { 309 int step = (milliseconds < 0) ? 0 : 5000; 310 bigtime_t d; 311 bigtime_t useconds = milliseconds * 1000; 312 int n, howmany; 313 314 if (useconds <= 0) /* we're here to go _through_ the loop */ 315 useconds = 1; 316 317 for (d = 0; d < useconds; d += step) { 318 n = 0; 319 howmany = ioctl(0, 'ichr', &n); 320 if (howmany >= 0 && n > 0) { 321 result = 1; 322 break; 323 } 324 if (useconds > 1 && step > 0) { 325 snooze(step); 326 milliseconds -= (step / 1000); 327 if (milliseconds <= 0) { 328 milliseconds = 0; 329 break; 330 } 331 } 332 } 333 } else if (milliseconds > 0) { 334 snooze(milliseconds * 1000); 335 milliseconds = 0; 336 } 337 #elif HAVE_SELECT 338 /* 339 * select() modifies the fd_set arguments; do this in the 340 * loop. 341 */ 342 FD_ZERO(&set); 343 344 #if !USE_KLIBC_KBD 345 if (mode & TW_INPUT) { 346 FD_SET(sp->_ifd, &set); 347 count = sp->_ifd + 1; 348 } 349 #endif 350 if ((mode & TW_MOUSE) 351 && (fd = sp->_mouse_fd) >= 0) { 352 FD_SET(fd, &set); 353 count = max(fd, count) + 1; 354 } 355 #ifdef NCURSES_WGETCH_EVENTS 356 if ((mode & TW_EVENT) && evl) { 357 for (n = 0; n < evl->count; ++n) { 358 _nc_event *ev = evl->events[n]; 359 360 if (ev->type == _NC_EVENT_FILE 361 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 362 FD_SET(ev->data.fev.fd, &set); 363 count = max(ev->data.fev.fd + 1, count); 364 } 365 } 366 } 367 #endif 368 369 #if USE_KLIBC_KBD 370 for (saved_set = set;; set = saved_set) { 371 if ((mode & TW_INPUT) 372 && (sp->_extended_key 373 || (KbdPeek(&ki, 0) == 0 374 && (ki.fbStatus & KBDTRF_FINAL_CHAR_IN)))) { 375 FD_ZERO(&set); 376 FD_SET(sp->_ifd, &set); 377 result = 1; 378 break; 379 } 380 381 tv.tv_sec = 0; 382 tv.tv_usec = (milliseconds == 0) ? 0 : (10 * 1000); 383 384 if ((result = select(count, &set, NULL, NULL, &tv)) != 0) 385 break; 386 387 /* Time out ? */ 388 if (milliseconds >= 0 && _nc_gettime(&t0, FALSE) >= milliseconds) { 389 result = 0; 390 break; 391 } 392 } 393 #else 394 if (milliseconds >= 0) { 395 struct timeval ntimeout; 396 ntimeout.tv_sec = milliseconds / 1000; 397 ntimeout.tv_usec = (milliseconds % 1000) * 1000; 398 result = select(count, &set, NULL, NULL, &ntimeout); 399 } else { 400 result = select(count, &set, NULL, NULL, NULL); 401 } 402 #endif 403 404 #ifdef NCURSES_WGETCH_EVENTS 405 if ((mode & TW_EVENT) && evl) { 406 evl->result_flags = 0; 407 for (n = 0; n < evl->count; ++n) { 408 _nc_event *ev = evl->events[n]; 409 410 if (ev->type == _NC_EVENT_FILE 411 && (ev->data.fev.flags & _NC_EVENT_FILE_READABLE)) { 412 ev->data.fev.result = 0; 413 if (FD_ISSET(ev->data.fev.fd, &set)) { 414 ev->data.fev.result |= _NC_EVENT_FILE_READABLE; 415 evl->result_flags |= _NC_EVENT_FILE_READABLE; 416 } 417 } else if (ev->type == _NC_EVENT_TIMEOUT_MSEC 418 && !result && timeout_is_event) 419 evl->result_flags |= _NC_EVENT_TIMEOUT_MSEC; 420 } 421 } 422 #endif 423 424 #endif /* USE_FUNC_POLL, etc */ 425 426 returntime = _nc_gettime(&t0, FALSE); 427 428 if (milliseconds >= 0) 429 milliseconds -= (int) (returntime - starttime); 430 431 #ifdef NCURSES_WGETCH_EVENTS 432 if (evl) { 433 evl->result_flags = 0; 434 for (n = 0; n < evl->count; ++n) { 435 _nc_event *ev = evl->events[n]; 436 437 if (ev->type == _NC_EVENT_TIMEOUT_MSEC) { 438 long diff = (returntime - starttime); 439 if (ev->data.timeout_msec <= diff) 440 ev->data.timeout_msec = 0; 441 else 442 ev->data.timeout_msec -= diff; 443 } 444 445 } 446 } 447 #endif 448 449 #if PRECISE_GETTIME && HAVE_NANOSLEEP 450 /* 451 * If the timeout hasn't expired, and we've gotten no data, 452 * this is probably a system where 'select()' needs to be left 453 * alone so that it can complete. Make this process sleep, 454 * then come back for more. 455 */ 456 if (result == 0 && milliseconds > 100) { 457 napms(100); /* FIXME: this won't be right if I recur! */ 458 milliseconds -= 100; 459 goto retry; 460 } 461 #endif 462 463 /* return approximate time left in milliseconds */ 464 if (timeleft) 465 *timeleft = milliseconds; 466 467 TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec", 468 result, errno, milliseconds)); 469 470 /* 471 * Both 'poll()' and 'select()' return the number of file descriptors 472 * that are active. Translate this back to the mask that denotes which 473 * file-descriptors, so that we don't need all of this system-specific 474 * code everywhere. 475 */ 476 if (result != 0) { 477 if (result > 0) { 478 result = 0; 479 #if USE_FUNC_POLL 480 for (count = 0; count < MIN_FDS; count++) { 481 if ((mode & (1 << count)) 482 && (fds[count].revents & POLLIN)) { 483 result |= (1 << count); 484 } 485 } 486 #elif defined(__BEOS__) 487 result = TW_INPUT; /* redundant, but simple */ 488 #elif HAVE_SELECT 489 if ((mode & TW_MOUSE) 490 && (fd = sp->_mouse_fd) >= 0 491 && FD_ISSET(fd, &set)) 492 result |= TW_MOUSE; 493 if ((mode & TW_INPUT) 494 && FD_ISSET(sp->_ifd, &set)) 495 result |= TW_INPUT; 496 #endif 497 } else 498 result = 0; 499 } 500 #ifdef NCURSES_WGETCH_EVENTS 501 if ((mode & TW_EVENT) && evl && evl->result_flags) 502 result |= TW_EVENT; 503 #endif 504 505 #if USE_FUNC_POLL 506 #ifdef NCURSES_WGETCH_EVENTS 507 if (fds != fd_list) 508 free((char *) fds); 509 #endif 510 #endif 511 512 return (result); 513 } 514